/* =============================================================================
   Čaroděj — components & site chrome.
   Tokens live in app.css; nothing here hardcodes a colour that is not a
   one-off gradient stop of the gilding.
   ============================================================================= */

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

.btn {
    display: inline-flex;
    gap: 0.6em;
    align-items: center;
    justify-content: center;
    padding: 0.78rem 1.7rem;
    font-family: var(--font-body);
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: 1;
    color: var(--color-text);
    text-decoration: none;
    text-transform: uppercase;
    letter-spacing: var(--tracking-widest);
    white-space: nowrap;
    cursor: pointer;
    background: none;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    transition:
        color var(--transition-fast),
        background-color var(--transition-fast),
        border-color var(--transition-fast),
        box-shadow var(--transition),
        transform var(--transition-fast);
}

.btn--sm {
    padding: 0.55rem 1.1rem;
    font-size: var(--text-2xs);
}

.btn--lg {
    padding: 1.05rem 2.5rem;
    font-size: var(--text-sm);
}

/* Gilded. Highlight at the top edge, shadow at the bottom — foil stamping,
   not a flat brand colour. Use at most once per view. */
.btn--primary {
    color: var(--color-text-inverse);
    background-image: linear-gradient(180deg, #f4e0b3 0%, #d8b36b 45%, #b78e44 100%);
    border-color: rgb(255 240 206 / 45%);
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / 45%),
        0 12px 28px -16px rgb(216 179 107 / 90%);
}

.btn--primary:hover {
    background-image: linear-gradient(180deg, #fbecd0 0%, #e5c384 45%, #c69c4f 100%);
    box-shadow:
        inset 0 1px 0 rgb(255 255 255 / 55%),
        0 16px 34px -14px rgb(216 179 107 / 100%);
}

.btn--secondary {
    color: var(--color-gold);
    background-color: rgb(216 179 107 / 4%);
    border-color: var(--color-gold-line);
}

.btn--secondary:hover {
    color: var(--color-gold-bright);
    background-color: var(--color-gold-wash);
    border-color: var(--color-gold-dim);
    box-shadow: var(--glow-gold);
}

.btn--ghost {
    color: var(--color-text-secondary);
}

.btn--ghost:hover {
    color: var(--color-gold);
    background-color: rgb(255 255 255 / 4%);
}

.btn:active:not(:disabled, [aria-disabled='true']) {
    transform: translateY(1px);
}

.btn:disabled,
.btn[aria-disabled='true'] {
    cursor: not-allowed;
    opacity: 0.58;
    box-shadow: none;
    transform: none;
}

.btn:disabled:hover,
.btn[aria-disabled='true']:hover {
    color: inherit;
    background-image: none;
    box-shadow: none;
}

.btn--primary:disabled,
.btn--primary[aria-disabled='true'] {
    color: var(--color-text-inverse);
    background-image: linear-gradient(180deg, #f4e0b3 0%, #d8b36b 45%, #b78e44 100%);
}

/* =============================================================================
   Cards
   ============================================================================= */

.card {
    position: relative;
    padding: var(--space-6);
    background-color: var(--color-surface);
    background-image: linear-gradient(180deg, rgb(255 255 255 / 4.5%), rgb(255 255 255 / 1.2%));
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    transition:
        border-color var(--transition),
        box-shadow var(--transition);
}

.card:hover {
    border-color: var(--color-gold-line);
    box-shadow:
        var(--shadow-md),
        inset 0 1px 0 rgb(240 216 162 / 14%),
        var(--glow-gold);
}

.card__eyebrow {
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--color-gold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-widest);
}

.card__title {
    margin-block-start: var(--space-2);
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 600;
    line-height: var(--leading-snug);
}

.card__body {
    margin-block-start: var(--space-3);
    color: var(--color-text-secondary);
}

.card__footer {
    margin-block-start: var(--space-5);
    padding-block-start: var(--space-4);
    border-block-start: 1px solid var(--color-border);
}

/* The ornamented variant used for a reading: brass corner brackets on two
   opposite corners, the way a plate is mounted in an atlas. */
.card--reading {
    padding: var(--space-7) var(--space-6) var(--space-6);
    background-image:
        radial-gradient(ellipse 70% 40% at 50% 0%, rgb(216 179 107 / 7%), transparent 70%),
        linear-gradient(180deg, rgb(255 255 255 / 4.5%), rgb(255 255 255 / 1.2%));
}

.card--reading::before,
.card--reading::after {
    position: absolute;
    width: 16px;
    height: 16px;
    border: 1px solid var(--color-gold-line);
    content: '';
}

.card--reading::before {
    inset-block-start: 10px;
    inset-inline-start: 10px;
    border-block-end: 0;
    border-inline-end: 0;
}

.card--reading::after {
    inset-block-end: 10px;
    inset-inline-end: 10px;
    border-block-start: 0;
    border-inline-start: 0;
}

/* =============================================================================
   Forms
   ============================================================================= */

.field {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.label {
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--color-text-secondary);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
}

.label__optional {
    font-weight: 400;
    color: var(--color-text-muted);
    text-transform: none;
    letter-spacing: 0;
}

.input,
.textarea,
.select {
    width: 100%;
    padding: 0.72rem 0.9rem;
    font-size: var(--text-base);
    color: var(--color-text);
    background-color: rgb(255 255 255 / 3.5%);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    transition:
        border-color var(--transition-fast),
        background-color var(--transition-fast),
        box-shadow var(--transition-fast);
}

.textarea {
    min-height: 8rem;
    line-height: var(--leading-normal);
    resize: vertical;
}

.select {
    padding-inline-end: 2.5rem;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%23d8b36b' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 0.95rem center;
    background-size: 12px;
    appearance: none;
}

.input::placeholder,
.textarea::placeholder {
    color: var(--color-text-muted);
    opacity: 1;
}

/* A single visible ring for pointer and keyboard alike — an input that only
   glows for keyboard users reads as broken to everyone else. */
.input:focus,
.textarea:focus,
.select:focus {
    background-color: rgb(255 255 255 / 5.5%);
    border-color: var(--color-gold-dim);
    outline: none;
    box-shadow: 0 0 0 3px rgb(216 179 107 / 20%);
}

.input:disabled,
.textarea:disabled,
.select:disabled {
    color: var(--color-text-muted);
    cursor: not-allowed;
    background-color: rgb(255 255 255 / 1.5%);
}

input[type='checkbox'],
input[type='radio'] {
    width: 1.05rem;
    height: 1.05rem;
    accent-color: var(--color-gold);
}

.choice {
    display: flex;
    gap: var(--space-3);
    align-items: center;
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}

/* A consent label runs to two lines; without this the flex item shrinks and the
   box stops being square. */
.choice input[type='checkbox'],
.choice input[type='radio'] {
    flex: none;
}

/* A set of radios — the four outcomes of a follow-up (spec sec. 63). Stacked
   rather than side by side: the options are whole sentences, and a row of four
   would wrap differently at every breakpoint. */
.choices {
    display: grid;
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.field__help {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.field__error {
    display: flex;
    gap: 0.5em;
    align-items: baseline;
    font-size: var(--text-sm);
    color: var(--color-danger);
}

.field__error::before {
    font-size: 0.8em;
    content: '\25c6';
}

.field--invalid .input,
.field--invalid .textarea,
.field--invalid .select {
    border-color: rgb(226 113 107 / 55%);
}

.field--invalid .input:focus,
.field--invalid .textarea:focus,
.field--invalid .select:focus {
    box-shadow: 0 0 0 3px rgb(226 113 107 / 20%);
}

/* =============================================================================
   Badge
   ============================================================================= */

.badge {
    display: inline-flex;
    gap: 0.45em;
    align-items: center;
    padding: 0.3em 0.75em;
    font-size: var(--text-2xs);
    font-weight: 600;
    line-height: 1.4;
    color: var(--color-gold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    background-color: var(--color-gold-wash);
    border: 1px solid var(--color-gold-line);
    border-radius: var(--radius-pill);
}

.badge--copper {
    color: var(--color-copper-hover);
    background-color: rgb(192 122 75 / 10%);
    border-color: var(--color-copper-line);
}

.badge--muted {
    color: var(--color-text-muted);
    background-color: rgb(255 255 255 / 3%);
    border-color: var(--color-border-strong);
}

.badge--solid {
    color: var(--color-text-inverse);
    background-image: linear-gradient(180deg, #f4e0b3, #c69c4f);
    border-color: transparent;
}

/* =============================================================================
   Ornamental divider — a ruled line broken by a small brass diamond.
   Used as <hr class="divider">, no children required.
   ============================================================================= */

.divider {
    position: relative;
    height: 1px;
    border: 0;

    /* The UA stylesheet hides overflow on <hr>, which would clip the diamond
       below to a one-pixel slice. */
    overflow: visible;
    background-image: linear-gradient(
        90deg,
        transparent,
        var(--color-gold-line) 16%,
        var(--color-gold-line) calc(50% - 12px),
        transparent calc(50% - 12px),
        transparent calc(50% + 12px),
        var(--color-gold-line) calc(50% + 12px),
        var(--color-gold-line) 84%,
        transparent
    );
}

.divider::after {
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: 50%;
    width: 8px;
    height: 8px;
    border: 1px solid var(--color-gold);
    transform: translate(-50%, -50%) rotate(45deg);
    content: '';
}

.divider--short {
    width: 12rem;
    margin-inline: auto;
}

/* =============================================================================
   Flash messages
   ============================================================================= */

.flash-stack {
    display: grid;
    gap: var(--space-3);
    margin-block: var(--space-5);
}

.flash {
    display: flex;
    gap: var(--space-3);
    align-items: baseline;
    padding: var(--space-4) var(--space-5);
    font-size: var(--text-sm);
    color: var(--color-text);
    background-color: var(--color-surface);
    background-image: linear-gradient(90deg, rgb(255 255 255 / 4%), transparent 60%);
    border: 1px solid var(--color-border);
    border-inline-start: 2px solid var(--flash-accent, var(--color-gold));
    border-radius: var(--radius-sm);
}

.flash::before {
    flex: none;
    font-size: 0.7em;
    color: var(--flash-accent, var(--color-gold));
    content: '\25c6';
}

.flash--success {
    --flash-accent: var(--color-success);
}

.flash--error,
.flash--danger {
    --flash-accent: var(--color-danger);
}

.flash--warning {
    --flash-accent: var(--color-warning);
}

.flash--info,
.flash--notice {
    --flash-accent: var(--color-info);
}

/* =============================================================================
   Section header & overline
   ============================================================================= */

.overline {
    display: inline-flex;
    gap: 0.85em;
    align-items: center;
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--color-gold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-widest);
}

.overline::before {
    inline-size: 1.75rem;
    block-size: 1px;
    background-image: linear-gradient(90deg, transparent, var(--color-gold));
    content: '';
}

.overline--centered {
    justify-content: center;
}

.overline--centered::after {
    inline-size: 1.75rem;
    block-size: 1px;
    background-image: linear-gradient(90deg, var(--color-gold), transparent);
    content: '';
}

.section-header {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    align-items: var(--section-header-align, flex-start);
}

.section-header--centered {
    --section-header-align: center;

    text-align: center;
}

.section-header__title {
    font-family: var(--font-display);
    font-size: var(--text-3xl);
    font-weight: 600;
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tight);
}

.section-header__lede {
    max-width: 54ch;
    color: var(--color-text-secondary);
}

/* =============================================================================
   Site chrome — public header & footer
   ============================================================================= */

.site-header {
    position: sticky;
    inset-block-start: 0;
    z-index: 50;
    background-color: rgb(10 13 24 / 72%);
    border-block-end: 1px solid var(--color-border);
    backdrop-filter: blur(14px) saturate(140%);
}

.site-header__inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4) var(--space-6);
    align-items: center;
    justify-content: space-between;
    min-block-size: 4.5rem;
    padding-block: var(--space-3);
}

.brand {
    display: inline-flex;
    gap: 0.7rem;
    align-items: center;
    color: var(--color-text);
    text-decoration: none;
}

.brand__mark {
    flex: none;
    color: var(--color-gold);
    transition: transform var(--transition-slow), filter var(--transition);
}

.brand:hover .brand__mark {
    filter: drop-shadow(0 0 10px rgb(216 179 107 / 55%));
    transform: rotate(45deg);
}

.brand__word {
    font-family: var(--font-display);
    font-size: 1.4rem;
    font-weight: 600;
    line-height: 1;
    text-transform: uppercase;
    letter-spacing: 0.14em;
}

.site-nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-6);
    align-items: center;
}

.site-nav__link {
    position: relative;
    padding-block: 0.4rem;
    font-size: var(--text-xs);
    font-weight: 500;
    color: var(--color-text-secondary);
    text-decoration: none;
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    transition: color var(--transition-fast);
}

.site-nav__link::after {
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    block-size: 1px;
    background-color: var(--color-gold);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--transition);
    content: '';
}

.site-nav__link:hover,
.site-nav__link[aria-current='page'] {
    color: var(--color-gold);
}

.site-nav__link:hover::after,
.site-nav__link[aria-current='page']::after {
    transform: scaleX(1);
}

.site-footer {
    margin-block-start: var(--space-9);
    padding-block: var(--space-7) var(--space-6);
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    background-image: linear-gradient(180deg, transparent, rgb(6 8 15 / 85%));
    border-block-start: 1px solid var(--color-border);
}

.site-footer__inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-5);
    align-items: center;
    justify-content: space-between;
}

.site-footer__brand {
    display: flex;
    gap: 0.6rem;
    align-items: center;
    font-family: var(--font-display);
    font-size: var(--text-lg);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--color-text-secondary);
}

.footer-nav {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-5);
}

.footer-nav a {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.footer-nav a:hover {
    color: var(--color-gold);
}

/* =============================================================================
   Site chrome — application shell (signed-in). Grows in M2.
   ============================================================================= */

.app-header {
    position: sticky;
    inset-block-start: 0;
    z-index: 50;
    background-color: rgb(10 13 24 / 84%);
    border-block-end: 1px solid var(--color-border);
    backdrop-filter: blur(14px);
}

.app-header__inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: center;
    justify-content: space-between;
    min-block-size: 3.6rem;
    padding-block: var(--space-2);
}

.app-header .brand__word {
    font-size: 1.15rem;
}

.user-chip {
    display: inline-flex;
    gap: 0.6rem;
    align-items: center;
    padding: 0.3rem 0.9rem 0.3rem 0.3rem;
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
    letter-spacing: var(--tracking-wide);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
}

/* The chip became a link in M2a; the global underline for <a> would turn it
   into a strange gilded pill. */
a.user-chip {
    text-decoration: none;
    transition:
        color var(--transition-fast),
        border-color var(--transition-fast);
}

a.user-chip:hover {
    color: var(--color-text);
    border-color: var(--color-gold-line);
}

.user-chip__avatar {
    display: grid;
    place-items: center;
    inline-size: 1.85rem;
    block-size: 1.85rem;
    font-size: var(--text-2xs);
    font-weight: 700;
    color: var(--color-text-inverse);
    background-image: linear-gradient(180deg, #f4e0b3, #b78e44);
    border-radius: 50%;
}

/* =============================================================================
   Site chrome — admin. Utilitarian on purpose: no ornament, no sky, denser
   rhythm, wider measure. Applied through the body_class block.
   ============================================================================= */

.is-admin {
    --container: 84rem;
    --text-base: 0.9375rem;

    background-image: none;
}

.is-admin::before,
.is-admin::after {
    display: none;
}

/* Headings in the body font, not the display serif.
   Cormorant is the brand's voice and it is the wrong voice here: these are
   labels on a control panel, not titles on a page somebody came to read, and at
   a glance a serif "Podle modelu" reads as prose the eye should slow down for.
   The rest of the site keeps it; this is the one area that is a tool. */
.is-admin h1,
.is-admin h2,
.is-admin h3,
.is-admin h4 {
    font-family: var(--font-body);
    font-weight: 600;
    letter-spacing: normal;
}

.admin-main h2 {
    margin-block-end: 0;
    font-size: var(--text-lg);
}

.admin-main h3 {
    margin-block-end: 0;
    font-size: var(--text-base);
}

.admin-header {
    background-color: var(--color-bg-deep);
    border-block-end: 1px solid var(--color-border-strong);
}

.admin-header__inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    align-items: center;
    justify-content: space-between;
    min-block-size: 3rem;
    padding-block: var(--space-2);
}

.admin-brand {
    font-family: var(--font-body);
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--color-text);
    text-decoration: none;
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
}

.admin-brand span {
    color: var(--color-gold);
}

.admin-whoami {
    display: flex;
    gap: var(--space-4);
    align-items: baseline;
    margin: 0;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

/* The shell: a fixed left rail and everything else. `minmax(0, 1fr)` rather
   than `1fr` is load-bearing — without it a wide table stretches the column
   instead of scrolling inside .table-scroll, and the whole page gets a
   horizontal scrollbar. */
.admin-shell {
    display: grid;
    grid-template-columns: 10rem minmax(0, 1fr);
    gap: var(--space-6);
    align-items: start;
    padding-block: var(--space-5);
}

@media (width < 48rem) {
    .admin-shell {
        grid-template-columns: minmax(0, 1fr);
    }
}

.admin-rail {
    position: sticky;
    inset-block-start: var(--space-4);
}

.admin-rail__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: 0;
    padding: 0;
    list-style: none;
}

@media (width < 48rem) {
    .admin-rail {
        position: static;
    }

    .admin-rail__list {
        flex-direction: row;
        flex-wrap: wrap;
    }
}

.admin-rail__link {
    display: block;
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--color-text-muted);
    text-decoration: none;
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    border-radius: var(--radius-sm);
}

.admin-rail__link:hover {
    color: var(--color-text);
    background-color: var(--color-surface);
}

.admin-rail__link.is-current {
    color: var(--color-gold);
    background-color: var(--color-gold-wash);
}

.admin-main {
    min-inline-size: 0;
}

/* Page heading inside the shell. Smaller than the site's section-header: this
   is a tool, and the title is a label rather than a piece of writing. */
.admin-head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-4);
    align-items: baseline;
    justify-content: space-between;
}

.admin-head h1 {
    margin: 0;
    font-size: var(--text-xl);
}

.admin-head__note {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

/* Sub-navigation within a section (AI: náklady / role / prompty / requesty). */
.admin-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    padding-block-end: var(--space-2);
    font-size: var(--text-xs);
    border-block-end: 1px solid var(--color-border);
}

.admin-tabs a {
    color: var(--color-text-muted);
    text-decoration: none;
}

.admin-tabs a:hover {
    color: var(--color-text);
}

.admin-tabs a.is-current {
    color: var(--color-gold);
}

/* KPI cards — the dashboard's one big number per box (spec sec. 106). Auto-fit
   rather than a fixed count so the row reflows without a media query per
   breakpoint. */
.kpi-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
    gap: var(--space-3);
}

.kpi {
    padding: var(--space-4);
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}

.kpi__label {
    margin: 0;
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--color-gold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
}

.kpi__value {
    margin: var(--space-2) 0 0;
    font-family: var(--font-mono);
    font-size: var(--text-2xl);
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
    color: var(--color-text);
}

.kpi__value--negative {
    color: var(--color-copper-hover);
}

.kpi__value--positive {
    color: var(--color-success);
}

.kpi__meta {
    margin: var(--space-1) 0 0;
    font-size: var(--text-2xs);
    color: var(--color-text-muted);
}

/* A standing caveat above a screen — the AI cost pages carry one because the
   price table they are computed from is admittedly a guess (config/packages/
   ai.yaml). Not a flash: it is a property of the page, not of the request. */
.admin-notice {
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    border: 1px solid var(--color-copper-line);
    border-inline-start: 3px solid var(--color-copper);
    border-radius: var(--radius-sm);
}

.admin-notice strong {
    color: var(--color-warning);
}

/* Filter bar above a list. */
.admin-filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    align-items: flex-end;
}

/* Simple LIMIT/OFFSET pager: previous, position, next. */
.admin-pager {
    display: flex;
    gap: var(--space-4);
    align-items: baseline;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

/* Key/value detail block — a payment, a request, a subscription. */
.admin-facts {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    gap: var(--space-2) var(--space-4);
    margin: 0;
    font-size: var(--text-sm);
}

.admin-facts dt {
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
}

.admin-facts dd {
    margin: 0;
    overflow-wrap: anywhere;
}

/* Read-only dump of a prompt body, a JSON schema or a webhook payload. */
.admin-pre {
    max-block-size: 28rem;
    margin: 0;
    padding: var(--space-3);
    overflow: auto;
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    background-color: var(--color-bg-deep);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}

/* Dense rows for the admin: the site-wide .data-table padding is generous for
   a four-column statement and wasteful for a fourteen-column request log. */
.data-table--dense th,
.data-table--dense td {
    padding: var(--space-2) var(--space-3);
}

/* A secondary line under a cell's main value — the raw event name under a
   Czech funnel label, a note under a ledger reason. `.field__help` is an inline
   element in a form, where it sits beside its control; in a table cell the two
   run together on one line and the eye cannot tell which is which. */
.data-table .field__help {
    display: block;
    margin-block-start: var(--space-1);
}

/* =============================================================================
   Auth pages — login, registration, password reset.
   Nothing new is invented here: the plate is a .card and every control comes
   from the form section above. These rules only give the four screens their
   measure and centre them.
   ============================================================================= */

.auth {
    padding-block: clamp(var(--space-6), 8vh, var(--space-9));
}

.auth__plate {
    max-width: 30rem;
    padding: clamp(var(--space-5), 5vw, var(--space-7));
    margin-inline: auto;
}

/* The card's hover lift belongs to things you click. A form you spend a minute
   typing into must not pulse under the cursor, so it is cancelled here. */
.auth__plate:hover {
    border-color: var(--color-border);
    box-shadow: var(--shadow-md);
}

/* The only full-width button in the design system: on a single-purpose form the
   primary action is the page, and a 12rem button floating in a 30rem plate
   reads as an afterthought. */
.auth__submit {
    width: 100%;

    /* On top of the .stack rhythm: the last thing above the button is usually a
       consent line whose text box ends flush with its own baseline, so the
       inherited gap reads tighter here than everywhere else. */
    margin-block-start: var(--space-3);
}

.auth__link,
.auth__footnote {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    text-align: center;
}

.auth__footnote {
    max-width: 30rem;
    margin-block-start: var(--space-5);
    margin-inline: auto;
}

/* =============================================================================
   Account page
   ============================================================================= */

.account-fact {
    display: grid;
    gap: var(--space-3);
    justify-items: start;
}

.account-code {
    font-size: var(--text-xl);
    font-weight: 600;
    color: var(--color-gold);
    letter-spacing: 0.28em;
}

/* The data-export requests on /ucet/gdpr. Still a <ul> for screen readers, but
   without markers: every row already opens with a status badge, and a bullet
   next to it reads as two bullets. */
.export-list {
    display: grid;
    gap: var(--space-4);
    width: 100%;
    padding: 0;
    margin: 0;
    list-style: none;
}

.export-list > li {
    display: grid;
    gap: var(--space-2);
    justify-items: start;
}

/* =============================================================================
   Landing page
   ============================================================================= */

.hero {
    position: relative;
    display: grid;
    place-items: center;
    min-block-size: min(72vh, 38rem);
    padding-block: clamp(2.5rem, 6vh, 4rem) clamp(2rem, 5vh, 3.5rem);
    overflow: hidden;
}

.hero__backdrop {
    position: absolute;
    inset: 0;
    pointer-events: none;
}

/* A watermark, not a motif: big enough to feel like an instrument etched into
   the paper, faint enough that the headline still owns the centre. */
.hero__ghost {
    position: absolute;
    inset-block-start: 29%;
    inset-inline-start: 50%;
    inline-size: min(80vw, 23rem);
    block-size: auto;
    color: var(--color-gold);
    opacity: 0.045;
    translate: -50% -50%;
    animation: drift 320s linear infinite;
}

.hero__constellation {
    position: absolute;
    color: var(--color-gold);
    opacity: 0.26;
}

.hero__constellation--top {
    inset-block-start: 0;
    inset-inline-end: -6%;
    inline-size: min(34rem, 56vw);
}

.hero__constellation--bottom {
    inset-block-end: 0;
    inset-inline-start: -9%;
    inline-size: min(22rem, 40vw);
    opacity: 0.18;
    scale: -1 1;
}

/* The plate: hairline margins down both sides, brass brackets on two
   opposite corners. */
.hero__plate {
    position: relative;
    padding: clamp(1.75rem, 4vw, 3rem) clamp(1.25rem, 4vw, 3rem);
    text-align: center;
}

.hero__plate::before,
.hero__plate::after {
    position: absolute;
    inline-size: 26px;
    block-size: 26px;
    border: 1px solid var(--color-gold-dim);
    content: '';
}

.hero__plate::before {
    inset-block-start: 0;
    inset-inline-start: 0;
    border-block-end: 0;
    border-inline-end: 0;
}

.hero__plate::after {
    inset-block-end: 0;
    inset-inline-end: 0;
    border-block-start: 0;
    border-inline-start: 0;
}

.hero__sigil {
    color: var(--color-gold);
    filter: drop-shadow(0 0 22px rgb(216 179 107 / 40%));
    margin-inline: auto;
}

.hero__overline {
    margin-block-start: var(--space-5);
}

.hero__title {
    margin-block-start: var(--space-4);
    font-size: var(--text-display);
    font-weight: 600;
    line-height: 1.04;
    letter-spacing: -0.02em;
}

/* The name itself is gilded — a single foil-stamped word in the headline. */
.hero__gild {
    display: inline-block;
    color: var(--color-gold);
    background-image: linear-gradient(170deg, #f6e4bb 10%, #d8b36b 52%, #b0873f 92%);
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

.hero__lede {
    max-width: 48ch;
    margin-block-start: var(--space-5);
    margin-inline: auto;
    font-size: var(--text-base);
    line-height: 1.72;
    color: var(--color-text-secondary);
}

.hero__lede strong {
    font-weight: 500;
    color: var(--color-text);
}

.hero__divider {
    width: 14rem;
    margin-block: var(--space-5);
    margin-inline: auto;
}

.hero__note {
    margin-block-start: var(--space-4);
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--color-gold-dim);
    text-transform: uppercase;
    letter-spacing: var(--tracking-widest);
}

/* The hero's question field (spec sec. 80). One input and one button, side by
   side where there is room and stacked where there is not — the input is what
   the page is for, so it takes the space and the button never wraps. */
.hero__ask {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    justify-content: center;
    max-width: 34rem;
    margin-inline: auto;
}

.hero__ask .input {
    flex: 1 1 18rem;
    padding-block: 0.95rem;
    text-align: center;
}

@media (min-width: 34rem) {
    .hero__ask .input {
        text-align: start;
    }
}

.hero__ask .btn {
    flex: 0 0 auto;
}

.hero__alt {
    margin-block-start: var(--space-4);
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

/* =============================================================================
   Homepage feature rows — one discipline per row: copy on one side, an
   engraved panel on the other, and a hairline between rows rather than card
   chrome around each. Cards would make five sections look like five products;
   these are meant to read as chapters.

     <article class="feature-row">
       <div class="feature-row__body stack">…</div>
       <div class="feature-row__art">…svg…</div>
     </article>
   ============================================================================= */

.feature-row {
    display: grid;
    gap: var(--space-6);
    align-items: center;
    padding-block: var(--space-7);
    border-block-start: 1px solid var(--color-border);
}

@media (min-width: 52rem) {
    .feature-row {
        grid-template-columns: 1.1fr 0.9fr;
    }

    /* Alternating sides, so a column of rows does not read as a list. The art
       moves rather than the copy: the text should always start at the same
       optical place down the page. */
    .feature-row--mirrored .feature-row__art {
        order: -1;
    }
}

.feature-row__art {
    display: grid;
    place-items: center;
    min-block-size: 12rem;
    padding: var(--space-6);
    background-image:
        radial-gradient(ellipse 60% 60% at 50% 40%, rgb(216 179 107 / 8%), transparent 70%);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}

.feature-row__glyph {
    inline-size: min(11rem, 60%);
    block-size: auto;
    color: var(--color-gold-dim);
}

/* `.feature-row--soon` — a muted body for a teaser that could not yet be bought
   — lived here until every row on the homepage led somewhere. It went with its
   last user (the horoscope row) rather than waiting for a next one: an unused
   modifier is a rule the next reader has to check for callers. The ceník still
   says „Připravujeme" where it has to, through `.badge--muted` and
   PricingController::READY_METHODS. */

/* =============================================================================
   "Jak to funguje" — three numbered steps, ruled rather than boxed.
   ============================================================================= */

.steps {
    display: grid;
    gap: var(--space-5);
    padding: 0;
    margin: 0;
    list-style: none;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    counter-reset: step;
}

.steps > li {
    padding-block-start: var(--space-4);
    border-block-start: 1px solid var(--color-gold-line);
    counter-increment: step;
}

.steps > li::before {
    display: block;
    margin-block-end: var(--space-3);
    font-family: var(--font-display);
    font-size: var(--text-3xl);
    font-weight: 600;
    line-height: 1;
    color: var(--color-gold);
    content: counter(step, decimal-leading-zero);
}

.step__name {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 600;
}

.step__note {
    margin-block-start: var(--space-2);
    color: var(--color-text-secondary);
}

/* =============================================================================
   "Ukázka výkladu" — a real reading, plainly labelled as a specimen.

   Set slightly smaller and inside a dashed rule, because the one thing this
   block must never do is read as the visitor's own reading.
   ============================================================================= */

.sample {
    position: relative;
    padding: var(--space-6);
    border: 1px dashed var(--color-gold-line);
    border-radius: var(--radius-md);
}

.sample__mark {
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: var(--space-5);
    padding-inline: var(--space-3);
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--color-gold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-widest);
    background-color: var(--color-bg);
    translate: 0 -50%;
}

.sample__question {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-style: italic;
    color: var(--color-text-secondary);
}

/* =============================================================================
   The lead magnet's card-of-the-day plate and address form (spec sec. 83).
   ============================================================================= */

.daily-card {
    display: grid;
    gap: var(--space-6);
    align-items: start;
}

@media (min-width: 40rem) {
    .daily-card {
        grid-template-columns: 12rem 1fr;
    }
}

.daily-card__plate {
    min-height: 15rem;
}

.lead-capture__form {
    max-width: 30rem;
}

/* The triad: three disciplines separated by hairlines, no card chrome.
   Stacked and ruled horizontally by default; the vertical rules only appear
   once all three really sit on one row, so no item ever carries a leading rule
   at the start of a line. */
.triad {
    display: grid;
    grid-template-columns: 1fr;
    margin: 0;
    padding: 0;

    /* F2a: the velká trojka really is a list of three, so the astrology page
       marks it up as one. The reset lives here rather than in that page,
       because a component that only survives inside a <div> is a component
       with an undocumented requirement. */
    list-style: none;
    border-block-start: 1px solid var(--color-border);
}

.triad__item {
    padding: var(--space-6) var(--space-5);
    text-align: center;
    border-block-end: 1px solid var(--color-border);
}

@media (min-width: 48rem) {
    .triad {
        grid-template-columns: repeat(3, 1fr);
        border-block-end: 1px solid var(--color-border);
    }

    .triad__item {
        border-block-end: 0;
        border-inline-start: 1px solid var(--color-border);
    }

    .triad__item:first-child {
        border-inline-start: 0;
    }
}

.triad__glyph {
    display: block;
    margin-inline: auto;
    font-size: var(--text-3xl);

    /* The astrological glyphs are ordinary Unicode and inherit the body face,
       which sets them at a weight the engraved look does not want. Nudged
       rather than restyled: they are text, not an icon set. */
    line-height: 1;
    color: var(--color-gold-dim);
    transition: color var(--transition), filter var(--transition);
}

.triad__item:hover .triad__glyph {
    color: var(--color-gold);
    filter: drop-shadow(0 0 14px rgb(216 179 107 / 45%));
}

.triad__name {
    margin-block-start: var(--space-4);
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.16em;
}

.triad__note {
    margin-block-start: var(--space-2);
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

/* =============================================================================
   Price plates — the ceník (spec sec. 68).

   A card that has to be scannable in a column of near-identical cards, so the
   only things allowed to be loud are the name and the number. Everything else
   is hairline and small-caps: the amount carries the gold, the card does not.

   Markup:

     <article class="card price-card">
         <p class="card__eyebrow">…</p>
         <h3 class="price-card__name">…</h3>
         <p class="price-card__amount">79 Kč <span class="price-card__unit">…</span></p>
         <p class="price-card__note">…</p>
         <p class="price-card__credits">…</p>
         <div class="price-card__cta">…</div>
     </article>
   ============================================================================= */

.price-grid {
    display: grid;
    gap: var(--space-5);
    grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
}

/* Column, so the call to action can be pushed to the bottom edge and every
   plate in a row ends on the same line however long the copy above it is. */
.price-card {
    display: flex;
    flex-direction: column;
}

.price-card__name {
    margin-block-start: var(--space-2);
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 600;
    line-height: var(--leading-snug);
}

.price-card__amount {
    margin-block-start: var(--space-4);
    font-family: var(--font-display);
    font-size: var(--text-3xl);
    font-weight: 600;
    line-height: var(--leading-tight);
    color: var(--color-gold-bright);
    letter-spacing: var(--tracking-tight);
}

.price-card__unit {
    font-family: var(--font-body);
    font-size: var(--text-sm);
    font-weight: 500;
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
}

.price-card__note {
    margin-block-start: var(--space-3);
    color: var(--color-text-secondary);
}

.price-card__credits {
    margin-block-start: var(--space-3);
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

/* margin-block-start: auto is what pins the button to the bottom of the plate. */
.price-card__cta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    align-items: center;
    margin-block-start: auto;
    padding-block-start: var(--space-5);
}

/* =============================================================================
   Data table — the credit statement, and anything else that is genuinely
   tabular. Ruled rather than striped: stripes on an ink background read as
   noise, and a hairline is the same device the rest of the system uses.

   Always wrapped in <div class="table-scroll"> so a narrow screen scrolls the
   table and not the page.
   ============================================================================= */

.table-scroll {
    overflow-x: auto;
}

.data-table {
    inline-size: 100%;
    font-size: var(--text-sm);
    border-collapse: collapse;
}

.data-table th,
.data-table td {
    padding: var(--space-3) var(--space-4);
    text-align: start;
    vertical-align: top;
    border-block-end: 1px solid var(--color-border);
}

.data-table th {
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--color-gold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    white-space: nowrap;
    border-block-end-color: var(--color-gold-line);
}

.data-table tbody tr:last-child td {
    border-block-end: 0;
}

.data-table__num {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    text-align: end;
    white-space: nowrap;
}

/* Sign carries the meaning in a ledger, so it carries the colour too. */
.data-table__num--positive {
    color: var(--color-success);
}

.data-table__num--negative {
    color: var(--color-copper-hover);
}

.data-table__when {
    color: var(--color-text-muted);
    white-space: nowrap;
}

/* =============================================================================
   Balance plate — one number, said once, at the top of /ucet/kredity.
   ============================================================================= */

.balance {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    align-items: baseline;
}

.balance__amount {
    font-family: var(--font-display);
    font-size: var(--text-4xl);
    font-weight: 600;
    line-height: var(--leading-tight);
    color: var(--color-gold-bright);
    letter-spacing: var(--tracking-tight);
}

.balance__unit {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
}

/* =============================================================================
   Notifications — the inbox at /ucet/oznameni (spec sec. 76).

   One rule, and it only has to do one thing: say which plates are already
   read. The unread ones are the default card, at full contrast; a read one
   steps back. That way "new" needs no colour of its own — the badge says it in
   words, and the contrast says it again for anyone skimming.

   Markup:

     <div class="card stack notification--read">…</div>
   ============================================================================= */

.notification--read .card__title,
.notification--read .card__body {
    color: var(--color-text-muted);
}

.notification--read .card__eyebrow {
    color: var(--color-text-muted);
}

/* =============================================================================
   The drawn cards — a row of engraved plates at the head of a reading
   (spec sec. 112).

   A card is typeset rather than illustrated: a tall plate, a hairline rule, the
   position above and the name below, in the display face. That is a deliberate
   choice and not a placeholder — an atlas of engraved plates is the aesthetic,
   and a row of grey image boxes waiting for pictures would look unfinished in a
   way that this does not.

   Since P8 one deck does carry a picture, and it did not arrive as photography:
   Tarot Čaroděje is *drawn*, in hairlines, in this same language, so `--drawn`
   below adds an engraving to the plate rather than replacing the plate with an
   image. Every other deck still typesets, and still should.

   Reversed cards are marked in the copy, never by rotating the plate. Turning
   the text upside down makes it unreadable to everybody and unreadable *twice*
   to a screen reader.

       <ol class="card-row">
         <li class="card-plate card-plate--reversed">
           <p class="card-plate__position">…</p>
           <p class="card-plate__name">…</p>
           <p class="card-plate__orientation">…</p>
         </li>
       </ol>
   ============================================================================= */

.card-row {
    display: grid;
    gap: var(--space-4);
    padding: 0;
    margin: 0;
    list-style: none;
    grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
}

.card-plate {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    align-items: center;
    justify-content: center;
    min-height: 11rem;
    padding: var(--space-4) var(--space-3);
    text-align: center;
    background-image:
        radial-gradient(ellipse 80% 50% at 50% 0%, rgb(216 179 107 / 9%), transparent 70%),
        linear-gradient(180deg, var(--color-surface-2), var(--color-surface));
    border: 1px solid var(--color-gold-line);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-sm), var(--glow-inset);
}

/* A copper hairline down the inner edge — the brass mount of a plate. */
.card-plate::before {
    position: absolute;
    inset: 5px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-xs);
    content: '';
    pointer-events: none;
}

.card-plate--reversed {
    border-color: var(--color-copper-line);
}

/* -----------------------------------------------------------------------------
   Tarot Čaroděje: the one deck that has a picture (P8).

   The plate above is unchanged and still does all the work — ground, border,
   brass mount, position above, name below. What `--drawn` adds is the engraving
   between them, so a card with a face and a card without are the same object
   with and without its picture rather than two different components.

   The face is SVG drawn from the card's own data
   (Divination\Service\Face\WizardTarotFace) and paints itself in `currentColor`,
   which is why the colour is set here and not in the markup: the plate decides
   what gold a card is struck in, and a reversed plate is struck in copper
   without the SVG knowing anything about it.
   -------------------------------------------------------------------------- */

.card-plate--drawn {
    justify-content: flex-start;
}

.tarot-face {
    display: block;
    width: 100%;

    /* The face keeps its own proportions rather than stretching into whatever
       room the plate has left. Flexing it looked reasonable until two cards in a
       row had names of different lengths — one wrapped, and the two engravings
       came out visibly different sizes, which reads as two decks rather than
       two cards. A fixed ratio makes the *plate* absorb the difference, which is
       what the plate is for. The number is the inline face's viewBox. */
    aspect-ratio: 200 / 212;
    height: auto;
    color: var(--color-gold);
}

.card-plate--reversed .tarot-face {
    color: var(--color-copper);
}

/* Which deck the row was read from — a footnote under the cards, not a badge.
   It is a fact about the reading, and the cards are the thing worth looking at. */
.card-row__deck {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    letter-spacing: 0.02em;
    text-align: center;
}

.card-plate__position {
    font-size: var(--text-2xs);
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
}

.card-plate__name {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    font-weight: 600;
    line-height: var(--leading-tight);
    color: var(--color-gold-bright);
}

.card-plate__orientation {
    font-size: var(--text-2xs);
    color: var(--color-copper-hover);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
}

/* The stave or the hexagram itself, above the name (spec sec. 49, 50).

   A rune and an I-ťing hexagram are characters rather than pictures, so the
   plate can carry the real mark instead of the grey image box a tarot plate is
   still waiting for — which is why this exists here and there is no equivalent
   for tarot or Lenormand.

   One stack for both scripts, because a browser falls back per character: Noto
   Sans Runic holds nothing but ᚠ–ᛟ, so a hexagram passes straight through it to
   the family that has U+4DC0–4DFF. In order after it, the families that really
   ship the hexagram block — Segoe UI Symbol on Windows, Apple Symbols on macOS
   and iOS, Noto Sans Symbols 2 on Linux — and then `serif` rather than the
   display face, which has neither block and would render a tofu box that looks
   like a bug.

   Noto Sans Runic is a webfont (see templates/base.html.twig for why that one
   and not the other); everything after it is whatever the reader already has.
   The line-height is 1 because these characters have no descenders and the
   plate is already vertically centred.

   `aria-hidden` in the markup, never here: hiding it in CSS would leave it in
   the accessibility tree. See templates/reading/detail.html.twig. */
.card-plate__glyph {
    font-family: 'Noto Sans Runic', 'Segoe UI Historic', 'Segoe UI Symbol', 'Apple Symbols', 'Noto Sans Symbols 2', serif;
    font-size: var(--text-3xl);
    line-height: 1;
    color: var(--color-gold);
}

/* The small line under the name where a plate has something to add: „12. karta"
   for a Lenormand card, „Voda nad ohněm" for a hexagram. Muted rather than
   copper, unlike the orientation above it — the orientation changes how the
   reading is read, this only says which card it is. The two never appear on the
   same plate. */
.card-plate__caption {
    font-size: var(--text-2xs);
    color: var(--color-text-muted);
    letter-spacing: var(--tracking-wide);
}

/* =============================================================================
   A reading's prose. One block per section, so the vertical rhythm of a
   thousand words is set once rather than by whatever the model's paragraph
   lengths happen to be.
   ============================================================================= */

.reading-section__title {
    margin-block-end: var(--space-3);
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 600;
    line-height: var(--leading-tight);
    color: var(--color-text);
}

/* The short answer is the one paragraph everybody reads, and often the only
   one somebody remembers a week later. Set larger, on gold, and given the
   plate's own ground. */
.reading-section--summary {
    padding: var(--space-5);
    background-color: var(--color-gold-wash);
    border: 1px solid var(--color-gold-line);
    border-radius: var(--radius-sm);
}

.reading-section--summary .reading-section__body {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    line-height: var(--leading-relaxed);
    color: var(--color-text);
}

.reading-section__body p {
    margin-block-end: var(--space-4);
}

.reading-section__body p:last-child {
    margin-block-end: 0;
}

/* The three lists. A marker per kind, because "co vám nahrává" and "na co si
   dát pozor" are opposite advice and must not look identical at a glance. */
.reading-list {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: 0;
    margin: 0;
    list-style: none;
}

.reading-list > li {
    position: relative;
    padding-inline-start: var(--space-5);
}

.reading-list > li::before {
    position: absolute;
    inset-inline-start: 0;
    color: var(--color-gold);
    content: '◆';
    font-size: var(--text-xs);
    line-height: 1.6;
}

.reading-list--positive > li::before {
    color: var(--color-success);
}

.reading-list--warning > li::before {
    color: var(--color-warning);
}

/* Follow-up questions are buttons, not bullets: clicking one starts the next
   reading with the question already written (spec sec. 113). */
.reading-followups {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
}

/* =============================================================================
   The waiting plate — what somebody looks at while the reading is written.
   ============================================================================= */

.reading-wait {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    align-items: center;
    padding-block: var(--space-7);
    text-align: center;
}

.reading-wait__title {
    font-family: var(--font-display);
    font-size: var(--text-3xl);
    font-weight: 600;
    color: var(--color-gold-bright);
}

/* A slow pulse rather than a spinner. A spinner says "this is taking too long";
   this says "something is happening", which is the truth about a job that
   takes half a minute. */
.reading-wait__sigil {
    color: var(--color-gold);
    animation: reading-pulse 3.2s ease-in-out infinite;
}

@keyframes reading-pulse {
    0%, 100% { opacity: 0.35; }
    50% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
    .reading-wait__sigil {
        animation: none;
        opacity: 0.8;
    }
}

/* =============================================================================
   The paywall (spec sec. 82).

   Sits directly under the last free section of a reading and has one job: make
   it obvious that the page stops rather than ends. The fade above it is what
   does that — a plate that simply began would read as the end of the reading,
   and somebody who thinks they have read it all does not pay.

   The fade is drawn by the wrapper, over the section above it, so no section
   template has to know it is the last free one.

     <div class="paywall-fade"></div>
     <div class="card paywall stack">…</div>
   ============================================================================= */

.paywall-fade {
    height: 5.5rem;

    /* Pulled up over the section above, so the text really does dissolve
       instead of a grey band appearing beneath it. The .stack gap is cancelled
       by the same amount. */
    margin-block: calc(var(--space-5) * -1 - 5.5rem) 0;
    background-image: linear-gradient(180deg, transparent, var(--color-bg) 92%);
    pointer-events: none;
}

.paywall {
    text-align: center;
    background-image:
        radial-gradient(ellipse 70% 60% at 50% 0%, rgb(216 179 107 / 12%), transparent 72%),
        linear-gradient(180deg, rgb(255 255 255 / 5%), rgb(255 255 255 / 1.2%));
    border-color: var(--color-gold-line);
}

.paywall:hover {
    box-shadow: var(--shadow-md);
}

.paywall__lock {
    margin-inline: auto;
    color: var(--color-gold);
}

.paywall__title {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 600;
    line-height: var(--leading-snug);
}

.paywall__price {
    font-family: var(--font-display);
    font-size: var(--text-4xl);
    font-weight: 600;
    line-height: var(--leading-tight);
    color: var(--color-gold-bright);
    letter-spacing: var(--tracking-tight);
}

.paywall__note {
    max-width: 44ch;
    margin-inline: auto;
    color: var(--color-text-secondary);
}

/* What is still behind it, as a list of headings without their text. Naming
   the parts is the offer; showing a word of them would be giving it away. */
.paywall__contents {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    justify-content: center;
    padding: 0;
    margin: 0;
    list-style: none;
}

.paywall__contents > li {
    padding: 0.3em 0.8em;
    font-size: var(--text-2xs);
    color: var(--color-text-muted);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    border: 1px dashed var(--color-border-strong);
    border-radius: var(--radius-pill);
}

/* =============================================================================
   The reading list at /vyklad/moje — a plate per reading, its status and the
   first sentence of it.
   ============================================================================= */

.reading-row__head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    align-items: baseline;
    justify-content: space-between;
}

.reading-row__teaser {
    display: -webkit-box;
    overflow: hidden;
    color: var(--color-text-secondary);
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
}

/* =============================================================================
   The Grimoár at /grimoar — a year of somebody's life as a ruled thread
   (spec sec. 64–65).

   The visual idea is the margin of a bound ledger: a hairline down the left,
   a brass stud per entry, months as engraved headings. It is the one screen in
   the application that is meant to look *long* — the length is the point, and
   an account with three years in it should feel like it has three years in it.
   ============================================================================= */

.timeline {
    --timeline-gutter: var(--space-6);
}

/* The month heading. Small caps and a gold hairline running off to the right,
   so the eye can find the year boundaries while scrolling past a decade. */
.timeline__month {
    display: flex;
    gap: var(--space-4);
    align-items: center;
    margin-bottom: var(--space-4);
    font-family: var(--font-body);
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--color-gold);
    letter-spacing: var(--tracking-widest);
    text-transform: uppercase;
}

.timeline__month::after {
    flex: 1;
    height: 1px;
    background: linear-gradient(to right, var(--color-gold-line), transparent);
    content: "";
}

/* The ruled margin. A pseudo-element rather than a border on the list so that
   it stops at the last entry instead of running under the month gap below. */
.timeline__entries {
    position: relative;
    padding-left: var(--timeline-gutter);
    margin: 0;
    list-style: none;
}

.timeline__entries::before {
    position: absolute;
    top: 0.6em;
    bottom: 0.6em;
    left: 5px;
    width: 1px;
    background: var(--color-border);
    content: "";
}

.timeline__entries > li + li {
    margin-top: var(--space-5);
}

/* The stud. Hollow for an ordinary entry, filled for one the customer answered
   — a Grimoár where the person wrote something back should show it at a
   glance. */
.timeline-entry {
    position: relative;
}

.timeline-entry::before {
    position: absolute;
    top: 0.55em;
    left: calc(-1 * var(--timeline-gutter) + 1px);
    width: 9px;
    height: 9px;
    border: 1px solid var(--color-gold-dim);
    border-radius: var(--radius-pill);
    background: var(--color-bg);
    content: "";
}

.timeline-entry--answered::before {
    border-color: var(--color-gold);
    background: var(--color-gold);
}

.timeline-entry__head {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    align-items: baseline;
}

.timeline-entry__when {
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    font-variant-numeric: tabular-nums;
}

.timeline-entry__title {
    margin: var(--space-1) 0 0;
    font-family: var(--font-display);
    font-size: var(--text-lg);
    line-height: var(--leading-snug);
    color: var(--color-text);
}

.timeline-entry__body {
    display: -webkit-box;
    overflow: hidden;
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
}

/* =============================================================================
   "Co si o vás Čaroděj pamatuje" (/grimoar/pamet). The rows reuse .export-list
   for their layout — a list of stacked blocks with no markers is exactly what
   that is — and add only the two things a memory needs that a GDPR request does
   not: the sentence set at reading size, and a hairline between rows so that a
   card of eight claims does not read as one paragraph.
   ============================================================================= */

.memory + .memory {
    padding-top: var(--space-4);
    border-top: 1px solid var(--color-border);
}

/* The claim itself. Larger than .field__help on purpose: this is the sentence
   the whole page exists to show, and the badges above it are the metadata. */
.memory__text {
    margin: 0;
    color: var(--color-text);
    font-size: var(--text-base);
    line-height: var(--leading-relaxed);
}

/* =============================================================================
   The theme statistics block (spec sec. 65) — "za poslední rok jste 17× řešil
   práci". A bar chart drawn in one gradient per row, because a chart library
   for five numbers would be a dependency for five numbers.
   ============================================================================= */

.theme-list {
    display: grid;
    gap: var(--space-3);
    margin: 0;
    padding: 0;
    list-style: none;
}

.theme-row {
    display: grid;
    grid-template-columns: minmax(6rem, 10rem) 1fr auto;
    gap: var(--space-3);
    align-items: center;
    font-size: var(--text-sm);
}

.theme-row__name {
    overflow: hidden;
    color: var(--color-text-secondary);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.theme-row__bar {
    height: 6px;
    border-radius: var(--radius-pill);
    background: var(--color-surface-2);
}

.theme-row__fill {
    display: block;
    height: 100%;
    border-radius: var(--radius-pill);
    background: linear-gradient(to right, var(--color-gold-deep), var(--color-gold));
}

.theme-row__count {
    color: var(--color-gold);
    font-variant-numeric: tabular-nums;
}

/* The filter chips above the timeline. A link per option, the active one
   inverted, and a "vše" that clears everything. */
.filter-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.filter-chip {
    padding: var(--space-1) var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    color: var(--color-text-secondary);
    font-size: var(--text-xs);
    text-decoration: none;
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

.filter-chip:hover {
    border-color: var(--color-gold-line);
    color: var(--color-text);
}

.filter-chip--active {
    border-color: var(--color-gold);
    background: var(--color-gold-wash);
    color: var(--color-gold-bright);
}

/* =============================================================================
   Chat — the AI Čaroděj (M11, spec sec. 58–60)

   The one screen in the application that is a conversation rather than a
   document, and the design has to say so without leaving the observatory: no
   speech-bubble tails, no avatars, no colour-coded chat app. Two registers
   instead — the person's words on a plain recessed plate, the Čaroděj's in the
   display serif on the surface the rest of the site uses for what it has
   written. The difference between "what you said" and "what was said to you" is
   then the same difference the site already makes between a form and a reading.
   ============================================================================= */

.chat-thread {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.chat-turn {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: 46ch;
}

/* Right for the person, left for the Čaroděj. margin-inline-start rather than
   align-self, so the plate keeps its own width and only its position moves. */
.chat-turn--user {
    margin-inline-start: auto;
    align-items: flex-end;
}

.chat-turn--wizard {
    margin-inline-end: auto;
}

.chat-turn__who {
    color: var(--color-text-muted);
    font-size: var(--text-2xs);
    letter-spacing: var(--tracking-wider);
    text-transform: uppercase;
}

.chat-turn__body {
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    line-height: var(--leading-normal);
    /* The person's own words survive their own line breaks. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

.chat-turn--user .chat-turn__body {
    background: var(--color-bg-deep);
    color: var(--color-text-secondary);
}

.chat-turn--wizard .chat-turn__body {
    border-color: var(--color-gold-line);
    background: var(--color-surface);
    box-shadow: var(--glow-inset);
    color: var(--color-text);
    font-family: var(--font-display);
    font-size: var(--text-lg);
}

/* "Čaroděj listuje vaším Grimoárem…" — the record that a tool ran. A rule with
   the sentence sitting on it: quieter than a message, and clearly not one. */
.chat-activity {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    font-style: italic;
}

.chat-activity::before,
.chat-activity::after {
    flex: 1;
    height: 1px;
    background: var(--color-gold-line);
    content: '';
    opacity: 0.5;
}

/* The compose box — the form itself, so the textarea, the counter and the
   button share one plate and read as a single control. */
.chat-compose {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    padding: var(--space-4);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-md);
}

.chat-compose .textarea {
    resize: vertical;
}

.chat-compose__actions {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* "Dnes máte ještě 3 zprávy zdarma." Small, factual and always visible: a price
   somebody finds out about afterwards is a price they complain about. */
.chat-quota {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--text-xs);
}

.chat-quota--paid {
    color: var(--color-warning);
}

/* One row of the thread list. The whole row is the link, so the target is the
   plate and not the four words on it. */
.conversation-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--color-border);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--transition-fast);
}

.conversation-row:hover {
    border-color: var(--color-gold-line);
}

.conversation-row:hover .conversation-row__title {
    color: var(--color-gold-bright);
}

.conversation-row__title {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    transition: color var(--transition-fast);
}

.conversation-row__when {
    flex: none;
    color: var(--color-text-muted);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
}

/* Voice — dictation into the composer and reading the answers back
   (spec sec. 61, F5c).

   Every control here is a `.btn.btn--ghost.btn--sm` and nothing more, on
   purpose. The primary gilding is already spent once per view on "Odeslat" —
   the design system allows exactly that — and a second gold control beside it
   would make the page look like it is asking for two things at once. Voice is an
   alternative way to do what the page already does, and it should read as one.

   The active state is a hairline in gold rather than a fill, which is the same
   rule the rest of the observatory follows: gold as a line, never as a plane. It
   is deliberately not red — a "recording" dot borrowed from a voice-memo app
   would be the one piece of somebody else's visual language on the site. */
/* The transcript and its read-aloud controls, as one block. It carries no
   layout of its own — the thread inside it already spaces its own turns — and
   exists so that the controller has something to bind to that can see both. */
.chat-transcript {
    display: block;
}

/* `:not([hidden])` because the read-aloud row on the thread ships with the
   `hidden` attribute and an author `display` would beat the user agent's
   `[hidden] { display: none }` — leaving two dead buttons on a page with
   JavaScript off, which is the one thing the attribute is there to prevent. */
.chat-voice:not([hidden]) {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

/* On the thread, the read-aloud controls sit above the transcript as their own
   quiet row rather than inline with anything. */
.chat-voice--thread {
    margin-bottom: var(--space-3);
}

.chat-voice__button {
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

.chat-voice__button--active {
    border-color: var(--color-gold-line);
    color: var(--color-gold-bright);
}

/* The microphone glyph. `currentColor` so it follows the button through its
   states, and `flex: none` so a long label cannot squash it. */
.chat-voice__icon {
    flex: none;
    color: currentcolor;
}

.chat-voice__note {
    margin: 0;
}

/* The per-answer play button, tucked under the Čaroděj's words rather than
   floated over them: the answers are long, and a control overlapping the last
   line of a reading is a control in the way of the thing it is about. */
.chat-turn__play {
    align-self: flex-start;
    margin-top: var(--space-2);
}

@media (width <= 40rem) {
    .chat-turn {
        max-width: 100%;
    }
}

/* Hands-free voice mode — voice v2 (spec sec. 61, P15).

   A strip above the compose box: one ghost button, a moving indicator, a line
   of status and, while somebody is talking, the live transcript under it. It is
   its own row rather than another control in the button cluster because the
   transcript needs the width, and a growing transcript in the same row as
   "Odeslat" would push the send button around the page while somebody spoke.

   The indicator is three gold bars, not a red dot. A recording light borrowed
   from a voice-memo app would be the only piece of somebody else's visual
   language on the site; gold moving on a hairline is this site's, and it is the
   same restraint .reading-wait__sigil shows for the same kind of waiting. */
/* `:not([hidden])` and not a bare `display`, here and on the wave below, and it
   is not a flourish: an author rule beats the user agent's `[hidden] { display:
   none }`, so a plain `display: flex` would make the whole panel visible in a
   browser with JavaScript off — the one case the `hidden` attribute in the
   template exists for. */
.voice-mode:not([hidden]) {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.voice-mode__bar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.voice-mode__button {
    transition: border-color var(--transition-fast), color var(--transition-fast);
}

.voice-mode__button--active {
    border-color: var(--color-gold-line);
    color: var(--color-gold-bright);
}

/* `currentColor` so it follows the button through its states, `flex: none` so a
   long label cannot squash it. */
.voice-mode__icon {
    flex: none;
    color: currentcolor;
}

.voice-mode__status {
    margin: 0;
    color: var(--color-text-muted);
    font-size: var(--text-xs);
}

.voice-mode--listening .voice-mode__status,
.voice-mode--speaking .voice-mode__status {
    color: var(--color-gold-dim);
}

/* A refused microphone or a room with nobody in it. Warning rather than danger:
   nothing was lost and nothing is broken, somebody just has to do something. */
.voice-mode--error .voice-mode__status {
    color: var(--color-warning);
}

.voice-mode__wave:not([hidden]) {
    display: inline-flex;
    align-items: flex-end;
    gap: 3px;
    height: 0.875rem;
}

.voice-mode__wave > span {
    width: 2px;
    height: 100%;
    border-radius: var(--radius-pill);
    background: var(--color-gold);
    transform: scaleY(0.25);
    transform-origin: bottom;
}

/* Listening: quick and uneven, the way a level meter moves. */
.voice-mode--listening .voice-mode__wave > span {
    animation: voice-wave 1.1s ease-in-out infinite;
}

.voice-mode--listening .voice-mode__wave > span:nth-child(2) {
    animation-delay: 0.18s;
}

.voice-mode--listening .voice-mode__wave > span:nth-child(3) {
    animation-delay: 0.36s;
}

/* Speaking: the same bars, half the speed. The difference is what tells
   somebody across the room whose turn it is without reading the line. */
.voice-mode--speaking .voice-mode__wave > span {
    animation: voice-wave 2.2s ease-in-out infinite;
}

.voice-mode--speaking .voice-mode__wave > span:nth-child(2) {
    animation-delay: 0.3s;
}

.voice-mode--speaking .voice-mode__wave > span:nth-child(3) {
    animation-delay: 0.6s;
}

/* Waiting on the model: no motion of its own, only the slow breath the reading
   page uses — because nothing is happening in the room, it is happening on a
   server, and a busy indicator would be claiming otherwise. */
.voice-mode--sending .voice-mode__wave > span {
    transform: scaleY(0.6);
    animation: reading-pulse 2.6s ease-in-out infinite;
}

@keyframes voice-wave {
    0%, 100% { transform: scaleY(0.25); }
    50% { transform: scaleY(1); }
}

/* The live transcript: a quiet italic behind a gold hairline, the same shape the
   thread gives a quoted thing. It is a guess being revised, and it is set to
   read as one — nobody should mistake it for what was sent. */
.voice-mode__heard {
    margin: 0;
    padding: var(--space-2) var(--space-3);
    border-left: 1px solid var(--color-gold-line);
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    background: var(--color-gold-wash);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-style: italic;
}

.voice-mode__note {
    margin: 0;
}

@media (prefers-reduced-motion: reduce) {
    .voice-mode--listening .voice-mode__wave > span,
    .voice-mode--speaking .voice-mode__wave > span,
    .voice-mode--sending .voice-mode__wave > span {
        transform: scaleY(0.6);
        animation: none;
    }
}

/* =============================================================================
   The public knowledge pages: /tarot/karty, /tarot/karty/{slug}, /snar and
   /snar/{slug} (spec sec. 87–90).

   These are the pages a stranger arrives on from a search result, and they are
   judged in about a second against whatever else was on that result page. So
   they are built out of the plates and rules the rest of the site already uses
   rather than out of a "content" style of their own — five new blocks, and four
   of them exist because a catalogue needs to be navigable in ways a reading
   never did.
   ============================================================================= */

/* Breadcrumbs: Tarot → Karty → Věž.

   Hairline-small and muted, because a breadcrumb is orientation and not
   navigation — it answers "where am I" for somebody who arrived sideways, and
   it is the trail a search engine prints under the result. The separator is the
   system's diamond, drawn by CSS so it is never read aloud between every pair
   of words.

       <nav class="breadcrumbs" aria-label="Drobečková navigace">
         <ol class="breadcrumbs__list">
           <li class="breadcrumbs__item"><a href="…">Tarot</a></li>
           <li class="breadcrumbs__item" aria-current="page">Věž</li>
         </ol>
       </nav> */
.breadcrumbs {
    padding-block-start: var(--space-6);
}

.breadcrumbs__list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    padding: 0;
    margin: 0;
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    list-style: none;
}

.breadcrumbs__item {
    display: flex;
    gap: var(--space-3);
    align-items: center;
    color: var(--color-text-muted);
}

.breadcrumbs__item + .breadcrumbs__item::before {
    color: var(--color-gold-dim);
    content: '\25C6';
    font-size: 0.5em;
}

.breadcrumbs__item a {
    color: var(--color-text-secondary);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.breadcrumbs__item a:hover {
    color: var(--color-gold-bright);
}

/* A card plate that is a link — the index pages, where seventy-eight plates are
   seventy-eight destinations.

   The anchor *is* the plate rather than sitting inside it, so the whole
   engraving is the target and not the two words on it. The hover is the one the
   .card already uses (gold line, gold glow), so a plate and a card answer a
   pointer the same way. */
.card-plate--link {
    text-decoration: none;
    transition:
        border-color var(--transition-fast),
        box-shadow var(--transition-fast),
        transform var(--transition-fast);
}

.card-plate--link:hover,
.card-plate--link:focus-visible {
    border-color: var(--color-gold-dim);
    box-shadow: var(--shadow-sm), var(--glow-inset), var(--glow-gold);
    transform: translateY(-2px);
}

@media (prefers-reduced-motion: reduce) {
    .card-plate--link:hover,
    .card-plate--link:focus-visible {
        transform: none;
    }
}

/* An index of many short names wants narrower plates than a spread of three:
   at 8.5rem a suit of fourteen wraps into rows of three on a laptop, which
   reads as a list rather than as a deck laid out. */
.card-row--index {
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
}

.card-row--index .card-plate {
    min-height: 8.5rem;
}

.card-row--index .card-plate__name {
    font-size: var(--text-lg);
}

/* The same auto-fit grid holding prose cards rather than plates: the four
   thematic tarot pages on /tarot/karty and the cross-links between them. A
   column has to fit two sentences instead of a card name, so the track is
   twice as wide — below about 34rem it collapses to one column, which is what
   a teaser wants anyway. */
.card-row--wide {
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
}

/* The head of an entry page: the plate on the left, the short reading beside
   it. Deliberately the same two-column arrangement as .daily-card — one card
   introduced by one paragraph is the same problem twice — and a separate rule
   rather than a second selector on that one, because the karta dne is a lead
   magnet and this is a catalogue entry, and the two will not always want the
   same measurements. */
.entry-head {
    display: grid;
    gap: var(--space-6);
    align-items: start;
}

@media (min-width: 40rem) {
    .entry-head {
        grid-template-columns: 12rem 1fr;
    }
}

.entry-head__plate {
    min-height: 15rem;
}

/* The facts strip under a heading: „Velká arkána · číslo 16 · živel oheň".
   Runs inline, wraps on a phone, and is separated by the same diamond the
   breadcrumbs use. */
.entry-facts {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-3);
    padding: 0;
    margin: 0;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
    list-style: none;
}

.entry-facts__item {
    display: flex;
    gap: var(--space-3);
    align-items: center;
}

.entry-facts__item + .entry-facts__item::before {
    color: var(--color-gold-dim);
    content: '\25C6';
    font-size: 0.5em;
}

/* The six changing lines on a hexagram page (spec sec. 50).

   A definition list in everything but the markup: a number, then a paragraph.
   The counter is *not* CSS-generated, because the numbers are content — „pátá
   čára" is what a reader arrives looking for, and a number that exists only in a
   stylesheet cannot be copied, searched for or read aloud.

   Two columns above a phone so the numbers form a rail the eye can jump down,
   one column below it, where a 5rem gutter would leave the text at forty
   characters. */
.hexagram-lines {
    display: grid;
    gap: var(--space-4);
    padding: 0;
    margin-block-start: var(--space-4);
    list-style: none;
}

.hexagram-lines__item {
    display: grid;
    gap: var(--space-1) var(--space-4);
}

@media (min-width: 40rem) {
    .hexagram-lines__item {
        grid-template-columns: 6rem 1fr;
    }
}

.hexagram-lines__number {
    font-size: var(--text-2xs);
    color: var(--color-gold-dim);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
}

.hexagram-lines__text {
    color: var(--color-text);
}

/* =============================================================================
   Legal pages — /obchodni-podminky, /ochrana-osobnich-udaju, /kontakt (M12).

   Long documents nobody reads for pleasure and everybody reads under pressure:
   somebody wants to know whether they can cancel, or what happens to their
   data. So the type is set for scanning rather than for atmosphere — numbered
   headings that can be cited, a measure short enough to track across, and
   tables where the answer is genuinely tabular.

   Built out of the tokens like everything else; the only new colour here is the
   one on .todo-legal, which is deliberately loud.
   ============================================================================= */

.legal {
    --stack-space: var(--space-5);
}

.legal__meta {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

.legal__section {
    max-inline-size: 62ch;
    padding-block-start: var(--space-6);
    border-block-start: 1px solid var(--color-border);
}

.legal__section > * + * {
    margin-block-start: var(--space-4);
}

.legal__section h2 {
    font-family: var(--font-display);
    font-size: var(--text-2xl);
    font-weight: 600;
    line-height: var(--leading-snug);
    color: var(--color-text);
}

.legal__section h3 {
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--color-text-secondary);
}

.legal__section p,
.legal__section li {
    line-height: var(--leading-relaxed);
    color: var(--color-text-secondary);
}

.legal__section ul {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding-inline-start: var(--space-5);
}

/* The operator's identity block and the like: a plate, so the details somebody
   came for are findable without reading the paragraph around them. */
.legal__party {
    padding: var(--space-5);
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}

.legal__party > * + * {
    margin-block-start: var(--space-3);
}

/* Wider than the prose it sits in — a three-column table squeezed to 62ch
   wraps every cell to two lines and stops being a table. */
.legal__table {
    inline-size: 100%;
    max-inline-size: none;
    font-size: var(--text-sm);
    border-collapse: collapse;
}

.legal__table th,
.legal__table td {
    padding: var(--space-3) var(--space-4);
    text-align: start;
    vertical-align: top;
    border-block-end: 1px solid var(--color-border);
}

.legal__table th {
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--color-gold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wider);
    border-block-end-color: var(--color-gold-line);
}

.legal__table td {
    color: var(--color-text-secondary);
}

.legal__table tbody tr:last-child td {
    border-block-end: 0;
}

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

/* The two sections that must not be skimmed past: the withdrawal clause and
   the entertainment disclaimer. Given the gilded ground the summary of a
   reading uses, which is the strongest "read this" the system has. */
.legal__section--highlight,
.legal__section--disclaimer {
    padding: var(--space-5);
    background-color: var(--color-gold-wash);
    border: 1px solid var(--color-gold-line);
    border-block-start-color: var(--color-gold-line);
    border-radius: var(--radius-sm);
}

/* The sentence spec sec. 130 fixes the wording of. Set in the display face at
   reading size so it carries as a statement rather than as fine print. */
.legal__disclaimer {
    font-family: var(--font-display);
    font-size: var(--text-xl);
    line-height: var(--leading-relaxed);
    color: var(--color-text);
}

/* Unfilled legal detail. Loud on purpose — copper on ink, in the mono face,
   with a rule under it — because the failure this guards against is somebody
   skimming the page before launch and not noticing that the IČO is a bracket.
   LegalPlaceholdersArePendingTest is the other half of the same guard. */
.todo-legal {
    padding: 0.1em 0.4em;
    font-family: var(--font-mono);
    font-size: 0.9em;
    color: var(--color-text-inverse);
    background-color: var(--color-warning);
    border-radius: var(--radius-xs);
}

/* =============================================================================
   The § 1837 notice by a payment button, and the standing disclaimer in the
   footer. Both are fine print in the literal sense: present, legible, and not
   competing with the thing they qualify.
   ============================================================================= */

.digital-consent {
    max-inline-size: 46ch;
    margin-block-start: var(--space-3);
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
    color: var(--color-text-muted);
}

.site-footer__disclaimer {
    margin-block-start: var(--space-5);
    font-size: var(--text-xs);
    line-height: var(--leading-normal);
    color: var(--color-text-muted);
}

/* =============================================================================
   Cookie notice — a statement, not a consent dialog. See
   assets/controllers/cookie_notice_controller.js for why there is no choice to
   offer.

   Because it is not a dialog it does not behave like one: no overlay, no focus
   trap, nothing blocked underneath. It sits at the bottom of the viewport,
   sized by its one sentence, and goes away for good when dismissed.
   ============================================================================= */

.cookie-notice {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: 60;
    padding-block: var(--space-3);
    background-color: rgb(6 8 15 / 94%);
    border-block-start: 1px solid var(--color-gold-line);
    backdrop-filter: blur(14px) saturate(140%);
}

.cookie-notice__inner {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-5);
    align-items: center;
    justify-content: space-between;
}

.cookie-notice__text {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}

.cookie-notice__button {
    flex: none;
}

/* The bar is fixed, so at the very bottom of a page it would sit on top of the
   footer — which is where the legal links and the disclaimer live, i.e. exactly
   what somebody following „Co to znamená" is scrolling towards.

   :has() rather than a class the controller toggles, so the reservation appears
   and disappears with the bar itself and there is no state to keep in step. It
   is also correctly absent for the visitor who has already dismissed it: the
   controller leaves the element `hidden`, the selector does not match, and no
   dead space is left behind. Browsers without :has() simply get the overlap
   they would have got anyway. */
body:has(.cookie-notice:not([hidden])) {
    padding-block-end: 5rem;
}

/* =============================================================================
   Numerology (spec sec. 44, 89).

   Four components, and all four exist because a number is not a word. The
   engraving vocabulary the rest of the site is built from — plates, hairlines,
   gold as a line — carries a card's *name* well and a single digit badly: set
   at body size, „7" is a typo, and set at display size it needs the arithmetic
   under it or it is an assertion.

   That arithmetic is the whole point of the module (spec sec. 45: the values are
   computed deterministically and the AI only interprets them), so .numeral and
   .numeral__steps are one idea in two selectors — a number and the sum that
   produced it — and neither should be used without the other.
   ============================================================================= */

.numeral-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
    gap: var(--space-4);
}

.numeral {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4);
    background-image: linear-gradient(180deg, var(--color-surface-2), var(--color-surface));
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}

/* ---------------------------------------------------------------------------
   Birth-place picker (F2a, spec sec. 37)

   The candidate list under the birth-place field. It is a plain <ul> rather
   than a <datalist>: a datalist cannot carry the latitude, longitude and
   timezone that choosing a place has to write into the form, and its rendering
   is the browser's rather than ours.

   Not absolutely positioned. An overlay would have to be measured, closed on
   outside clicks and kept away from the viewport edge; pushing the rest of the
   form down for a moment costs nothing on a page whose next control is a
   submit button, and it cannot be clipped by an ancestor's overflow.
   --------------------------------------------------------------------------- */
.place-results {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin: var(--space-2) 0 0;
    padding: 0;
    overflow: hidden;
    list-style: none;
    background: var(--color-border);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
}

/* The digit itself, in the display face and on gold. Tabular figures so that a
   „11/2" and a „7" beside each other sit on the same optical baseline width
   rather than drifting — the grid puts them in a column and proportional
   numerals would make that column look bent. */
.numeral__value {
    font-family: var(--font-display);
    font-size: var(--text-4xl);
    font-weight: 600;
    line-height: 1;
    color: var(--color-gold-bright);
    font-variant-numeric: tabular-nums;
}

.numeral__heading {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--color-text);
}

.numeral__note {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}

/* The shown work. Monospaced, because the terms of a sum have to line up and
   because it should read as a calculation rather than as more prose — the one
   place on the site where the body face would be actively misleading.

   It scrolls rather than wraps: „2 + 9 + 1 + 2 + 1 + 9 + 6 + 8 = 38 → 3 + 8 = 11"
   broken across two lines is two sums, and somebody checking it on a phone would
   have to work out where the break was. */
.numeral__steps {
    margin-block-start: auto;
    padding-block-start: var(--space-2);
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    line-height: var(--leading-relaxed);
    color: var(--color-text-muted);
    border-block-start: 1px solid var(--color-border);
}

/* One reduction round. A block, so a two-step reduction is two lines instead of
   one line too wide for the card — the tail of a clipped sum is exactly the half
   a reader wants. `nowrap` and its own scroll container, so the one genuinely
   long sum (eight digits of a birth date) slides instead of folding, which
   would read as two sums. */
.numeral__step {
    display: block;
    overflow-x: auto;
    white-space: nowrap;
}

/* The same typewriter, inline in a sentence — the transliterated name in the
   „počítáno z" note. No rule above it and no block spacing: it is a word in a
   paragraph, not a derivation of its own. */
.numeral__mono {
    font-family: var(--font-mono);
    font-size: 0.9em;
    color: var(--color-text-secondary);
}

/* One person from „lidé ve vašem životě" against the account's own number. A
   row and not a card: this is a list somebody scans for a name, and twelve
   plates would bury it. */
.match-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    align-items: baseline;
    justify-content: space-between;
    padding-block: var(--space-3);
    border-block-end: 1px solid var(--color-border);
}

.match-row:last-child {
    border-block-end: 0;
}

.match-row__who {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    color: var(--color-text);
}

.match-row__note {
    font-size: var(--text-sm);
    color: var(--color-text-muted);
}

/* A plate whose name is a number.

   `.card-plate__name` is set for a card's title — „Věž", „Velekněžka" — and at
   that size a lone digit reads as a caption rather than as the subject of the
   page it is on. The numerology plates carry one or three characters where the
   tarot plates carry a word, so they get the display size the digit deserves,
   with tabular figures so that „11/2" and „7" sit on the same optical width in
   a grid. */
.card-plate__name--numeral {
    font-size: var(--text-3xl);
    font-variant-numeric: tabular-nums;
}

/* =============================================================================
   The dream journal at /sny (spec sec. 46–48).

   Almost nothing new. The journal is the Grimoár's timeline with a different
   kind of row in it, the recurring motifs are the theme-statistics bar chart
   with a different query behind it, and both are reused as they stand — a
   second visual language for the same idea would make the two pages look like
   two products.

   What is here is the one thing the Grimoár has no equivalent of: a dream shown
   in full, as the person wrote it, line breaks and all.
   ============================================================================= */

/* Somebody's own words, at the length they wrote them. Slightly looser than the
   body default and capped in width, because a remembered dream is one long
   paragraph without punctuation and it is unreadable at full container width.

   `white-space` is deliberately not `pre-wrap`: the text arrives through
   `|nl2br`, so the breaks are already <br>, and pre-wrap on top of that would
   double every blank line. */
.dream-text {
    max-width: 62ch;
    color: var(--color-text-secondary);
    line-height: var(--leading-relaxed);
}

/* The `?hledat=` filter above the journal. `.input` is width:100% everywhere
   else, which is right for a form field in a column and wrong for one sitting
   next to its own button — it pushes the button onto a line of its own. */
.journal-search {
    align-items: center;
}

.journal-search__input {
    flex: 1 1 18rem;
    max-width: 26rem;
}

.place-option {
    display: flex;
    gap: var(--space-3);
    align-items: baseline;
    justify-content: space-between;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    font: inherit;
    font-size: var(--text-sm);
    color: var(--color-text);
    text-align: start;
    cursor: pointer;
    background: var(--color-surface);
    border: 0;
    transition: background var(--transition-fast), color var(--transition-fast);
}

.place-option:hover,
.place-option:focus-visible {
    color: var(--color-bg);
    background: var(--color-gold);
}

/* The timezone is the half of the answer nobody expects, and the thing somebody
   born abroad should check. Quiet, but present. */
.place-option__zone {
    flex: none;
    font-family: var(--font-mono);
    font-size: var(--text-2xs);
    color: var(--color-text-secondary);
}

.place-option:hover .place-option__zone,
.place-option:focus-visible .place-option__zone {
    color: var(--color-bg);
}

/* ------------------------------------------------------------------------- */
/* Chart wheel (F2c, spec sec. 75)                                            */
/* ------------------------------------------------------------------------- */

/* The wrapper, not the drawing. Everything about how the wheel looks is stated
   on the SVG's own elements — see Astrology\Service\Wheel\WheelCanvas for why:
   the same markup is handed to mpdf, which inherits no styles, resolves no
   custom properties and honours no classes. Anything expressed here would be
   a difference between the web version and the PDF one, which is the bug that
   arrangement exists to prevent.

   So this does one thing: it makes the drawing fit. The SVG carries a pixel
   width, and on a narrow screen that width is wider than the card. */
.chart-wheel {
    display: flex;
    justify-content: center;
    margin-block: var(--space-4);
}

.chart-wheel svg {
    inline-size: 100%;
    block-size: auto;

    /* The wheel stops growing before the type on it starts looking like a
       poster. Beyond about 30rem the glyphs are large and the aspect lines are
       still hairlines, which reads as a diagram somebody zoomed into. */
    max-inline-size: 30rem;
}

/* =============================================================================
   Product motif — the "photograph" of a digital product (PayU onboarding).

   An engraved emblem at the head of each price card, in the same thin gold
   strokes as the sigil. Decorative: aria-hidden in the component, sized here.
   ============================================================================= */

.product-motif {
    display: block;
    width: 48px;
    height: 48px;
    margin-block-end: var(--space-3);
    color: var(--color-gold);
    opacity: 0.85;
}

.product-motif svg {
    display: block;
    width: 100%;
    height: 100%;
}

/* --------------------------------------------------------------------------
   Persony (spec sec. 60)
   -------------------------------------------------------------------------- */

/* One character to choose from, on the profile. A plate rather than a radio in
   a row, because the sentence under the name is what somebody actually decides
   on — four bare names would be picked by the sound of the word.

   The radio itself stays in the flow and stays focusable; it is the label that
   is the target, so a click anywhere on the plate selects. */
.persona-choices {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.persona-choice {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface-2);
    cursor: pointer;
    transition: border-color var(--transition), background var(--transition);
}

.persona-choice:hover,
.persona-choice:focus-within {
    border-color: var(--color-gold-line);
}

.persona-choice--on {
    border-color: var(--color-gold);
    background: var(--color-gold-wash);
}

.persona-choice input {
    margin-block-start: 0.2em;
    flex: none;
    accent-color: var(--color-gold);
}

/* The sigil variant. Gold and small — an avatar in the sense spec sec. 60 asks
   for, without a face or a photograph. */
.persona-choice__mark {
    flex: none;
    color: var(--color-gold);
    line-height: 0;
}

.persona-choice__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.persona-choice__name {
    font-family: var(--font-display);
    font-size: var(--text-lg);
    color: var(--color-text);
}

.persona-choice__note {
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    color: var(--color-text-secondary);
}

/* The persona select on the chat compose box, and the sentence under it. */
.chat-compose__persona {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* The mark beside a thread's persona name in its heading. Inline, so it sits on
   the same line as the name without disturbing the overline's rhythm. */
.persona-mark {
    display: inline-block;
    vertical-align: -0.2em;
    margin-inline-end: var(--space-1);
    color: var(--color-gold);
    line-height: 0;
}

/* --------------------------------------------------------------------------
   Velký výklad — jedna sekce na metodu (spec sec. 57)
   -------------------------------------------------------------------------- */

/* A synthesis section carries an eyebrow naming the method it speaks for, and
   the left rule is what makes the views read as separate voices while scrolling
   a long page. Deliberately quieter than the summary plate above them: the
   short answer is still the thing somebody remembers. */
.reading-section--view {
    border-inline-start: 2px solid var(--color-gold-line);
}

/* --------------------------------------------------------------------------
   Dárkové poukazy (spec sec. 84)
   -------------------------------------------------------------------------- */

/* The code on the gifts page. Monospace and letterspaced hard, because it is
   eight characters somebody is going to copy by hand — the same treatment the
   certificate gives it. */
.voucher-code {
    margin: 0;
    font-family: var(--font-mono);
    font-size: var(--text-xl);
    letter-spacing: 0.28em;
    color: var(--color-gold);
}

/* The dedication, set as the quotation it is. */
.voucher-message {
    margin: 0;
    font-family: var(--font-display);
    font-style: italic;
    color: var(--color-text-secondary);
}

/* The two optional boxes on a gift card in the ceník. Stacked like the fields
   they are, rather than crammed beside the button — a dedication is a sentence
   somebody thinks about, not a checkbox. */
.gift-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
    width: 100%;
    text-align: start;
}

.gift-form .textarea {
    resize: vertical;
}
