/* =============================================================================
   Čaroděj — design system, layer 1: fonts, tokens, reset, base type, layout.

   Aesthetic direction: "řezba" — a carved, gilded night. Deep ink-blue ground
   (#050b15), warm brass hairlines, engraved illustrations bleeding in from the
   page edges, and two typefaces doing one job each: Fraunces (a soft-serif
   with an optical size axis) for anything that speaks, Montserrat for anything
   that labels. Dark-only by design; there is no light theme and none is
   planned, so `color-scheme: dark` is stated once and never conditioned.

   The tokens below are taken verbatim from the delivered mockup
   (design-rezba/css/style.css) wherever the mockup had an opinion. Everything
   the mockup did not cover — spacing, status colours, the type scale for
   subpages — is derived from it and marked as such, so a later reader can tell
   what was given from what was inferred.

   Loading order (assets/app.js):
       app.css  →  components.css  →  pages/*.css
   `app.css` may not name a component; `components.css` may not name a page.

   See docs/02-development/04-design-system.md for the full rulebook and
   /dev/styleguide for a rendered catalogue of every class defined here.
   ============================================================================= */

/* =============================================================================
   Fonts — self-hosted, so `font-src 'self'` covers them.

   Both files are variable (one axis of weight each, 100–900), which is why a
   single @font-face per family carries the whole range: asking for weight 390
   or 630 in a rule below is a real instance, not a synthesised one. Czech
   diacritics were verified in both files before they were accepted.

   `font-display: swap` rather than `optional`: the page is mostly type, and a
   flash of Georgia is better than a paragraph that never arrives in Fraunces.
   ============================================================================= */

@font-face {
    font-family: 'Fraunces';
    src: url("../fonts/fraunces-variable-g28FJ1C.ttf") format('truetype');
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
}

@font-face {
    font-family: 'Montserrat';
    src: url("../fonts/montserrat-variable-2a_PCE0.ttf") format('truetype');
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
}

/* =============================================================================
   Tokens
   ============================================================================= */

:root {
    color-scheme: dark;

    /* --- Ground ---------------------------------------------------------- *
     * From the mockup. `--color-background` is the page; `--color-band` is the
     * slightly lifted strip the tarot section sits on and the value any full-
     * width band should use when it needs to read as a separate plane.        */
    --color-background: #050b15;
    --color-background-deep: #030812;
    --color-band: #090c19;
    --color-surface: rgb(18 18 35 / 88%);
    --color-surface-light: rgb(28 28 43 / 90%);

    /* Derived: the two gradients every raised plate in the system uses. Named
       rather than repeated, because "a panel" and "a card" and "a price plate"
       are the same physical object seen at three sizes. */
    --plate-fill: linear-gradient(115deg, rgb(24 21 38 / 95%), rgb(11 15 29 / 92%));
    --plate-fill-soft: linear-gradient(115deg, rgb(24 21 38 / 62%), rgb(11 15 29 / 58%));
    --plate-shadow: 0 7px 24px rgb(0 0 0 / 20%), inset 0 0 28px rgb(57 43 78 / 8%);

    /* --- Text ------------------------------------------------------------ *
     * `--color-muted` is the floor: #aaa6a4 on #050b15 measures 8.4:1, so any
     * muted text in the system clears AA for body copy with room to spare.
     * Nothing dimmer than this is allowed to carry a sentence.                */
    --color-text: #f1eee9;
    --color-text-soft: #ddd8d1;
    --color-muted: #aaa6a4;
    --color-text-inverse: #090913;

    /* --- Brass ----------------------------------------------------------- */
    --color-gold: #c8914c;
    --color-gold-light: #e0af67;
    --color-gold-bright: #f1c984;
    --color-accent-text: #e0902d;

    /* --- Borders --------------------------------------------------------- */
    --color-border: rgb(157 105 55 / 72%);
    --color-border-soft: rgb(153 105 59 / 30%);
    --color-border-neutral: rgb(127 126 137 / 45%);

    /* --- Status ---------------------------------------------------------- *
     * Derived. Desaturated towards the brass so a green tick does not look
     * like it was pasted in from another application; each still clears AA on
     * the page ground.                                                        */
    --color-success: #79c294;
    --color-warning: #e0a85b;
    --color-danger: #e58079;
    --color-info: #8fb3dd;

    /* --- Type ------------------------------------------------------------ */
    --font-serif: 'Fraunces', Georgia, 'Times New Roman', serif;
    --font-sans: 'Montserrat', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --font-mono: ui-monospace, 'SFMono-Regular', 'Cascadia Mono', Menlo, Consolas, monospace;

    /* Derived scale. Display sizes are fluid because a 70px h1 on a phone is
       not a heading, it is a wall. */
    --text-2xs: 0.6875rem;  /* 11 */
    --text-xs: 0.75rem;     /* 12 */
    --text-sm: 0.8125rem;   /* 13 */
    --text-base: 0.9375rem; /* 15 */
    --text-md: 1rem;        /* 16 */
    --text-lg: 1.125rem;    /* 18 */
    --text-xl: 1.375rem;    /* 22 */
    --text-2xl: clamp(1.5rem, 1.2rem + 1.2vw, 2rem);       /* 24 → 32 */
    --text-3xl: clamp(1.75rem, 1.3rem + 1.9vw, 2.375rem);  /* 28 → 38 */
    --text-4xl: clamp(2rem, 1.4rem + 2.6vw, 2.875rem);         /* 32 → 46 */
    --text-display: clamp(2.375rem, 1.1rem + 5.2vw, 4.375rem); /* 38 → 70 */

    /* --- Spacing (4px base) ---------------------------------------------- */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;
    --space-8: 4rem;
    --space-9: 5.5rem;

    /* --- Radii ------------------------------------------------------------ *
     * One radius from the mockup for plates, one smaller for controls. Nothing
     * in this system is round.                                                */
    --radius: 11px;
    --radius-control: 6px;
    --radius-sm: 4px;
    --radius-pill: 999px;

    /* --- Widths ----------------------------------------------------------- */
    --header-width: 1370px;
    --content-wide: 1450px;
    --content: 1140px;
    --content-narrow: 760px;

    /* --- Motion ----------------------------------------------------------- */
    --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
    --transition-fast: 160ms ease;
    --transition: 240ms var(--ease);

    /* --- Recurring decorations -------------------------------------------- *
     * The gold hairline that closes a section in the mockup, and the brass
     * gradient a "current" nav item wears. Both appear in several components,
     * so they are declared once here.                                         */
    --rule-gold: linear-gradient(90deg, transparent, rgb(148 99 50 / 40%) 15%, rgb(148 99 50 / 40%) 85%, transparent);
    --rule-gold-fade: linear-gradient(90deg, transparent, rgb(180 123 60 / 45%));
}

/* =============================================================================
   Reset
   ============================================================================= */

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

html {
    color-scheme: dark;
    scroll-behavior: smooth;
    -webkit-text-size-adjust: 100%;
    scrollbar-color: rgb(200 145 76 / 35%) transparent;
    scrollbar-width: thin;
}

/* Flex column so a short page (a 404, a one-field form) still puts its footer
   at the bottom of the window instead of halfway up it. The two pseudo-elements
   below are `position: fixed` and therefore out of flow — they never become
   flex items. */
body {
    display: flex;
    flex-direction: column;
    min-width: 320px;
    min-height: 100dvh;
    margin: 0;
    overflow-x: hidden;
    font: 400 var(--text-md) / 1.6 var(--font-sans);
    color: var(--color-text);
    background: var(--color-background);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* The star field. Three tiles at three densities, fixed so it reads as sky
   behind the page rather than as a texture printed on it. It is decoration and
   nothing else depends on it, so it is on a pseudo-element that
   `prefers-reduced-transparency` or a future opt-out could drop wholesale.

   The homepage paints its own sky inside .hero / .tarot / .offerings and sets
   `--sky: none` to switch this one off, so the two never double up. */
body::before {
    position: fixed;
    z-index: -1;
    inset: 0;
    background-image: var(--sky,
        radial-gradient(circle at 28% 17%, rgb(255 221 160 / 90%) 0 1px, transparent 1.5px),
        radial-gradient(circle at 67% 27%, rgb(255 218 145 / 75%) 0 1px, transparent 1.5px),
        radial-gradient(circle at 45% 69%, rgb(195 143 81 / 48%) 0 1px, transparent 1.5px));
    background-size: 207px 183px, 263px 227px, 171px 159px;
    opacity: 0.34;
    pointer-events: none;
    content: '';
}

body > main {
    flex: 1 0 auto;
}

img,
svg,
video,
canvas {
    display: block;
    max-width: 100%;
}

a {
    color: inherit;
    text-decoration: none;
}

button,
input,
select,
textarea {
    font: inherit;
    color: inherit;
}

button {
    background: none;
    border: 0;
}

figure,
p,
h1,
h2,
h3,
h4,
h5,
h6,
blockquote,
dl,
dd,
ol,
ul {
    margin: 0;
}

:target {
    scroll-margin-block-start: var(--space-8);
}

/* =============================================================================
   Base typography

   Fraunces for headings, Montserrat for everything else. The weights are low
   on purpose — the mockup sets its display type at 390–430, and a 70px heading
   at weight 600 in this typeface reads as shouting rather than as engraving.
   ============================================================================= */

h1,
h2,
h3,
h4,
h5,
h6 {
    font-family: var(--font-serif);
    font-weight: 400;
    line-height: 1.16;
    letter-spacing: -0.015em;
    color: var(--color-text);
    text-wrap: balance;
}

h1 {
    font-size: var(--text-3xl);
    font-weight: 430;
}

h2 {
    font-size: var(--text-2xl);
    font-weight: 390;
}

h3 {
    font-size: var(--text-xl);
    font-weight: 400;
    letter-spacing: 0;
}

h4,
h5,
h6 {
    font-size: var(--text-lg);
    letter-spacing: 0;
}

p,
li {
    text-wrap: pretty;
}

strong,
b {
    font-weight: 620;
    color: var(--color-text);
}

small {
    font-size: var(--text-sm);
}

code,
kbd,
samp,
pre {
    font-family: var(--font-mono);
    font-size: 0.9em;
}

hr {
    height: 1px;
    border: 0;
    background: var(--rule-gold);
}

/* Links inside running text get an underline. Chrome opts out by not being
   running text: a bare `li a` would catch the admin rail, the tabs and the
   breadcrumbs, which are lists of controls rather than sentences, so list items
   are underlined only inside a prose container. `.button` and `.badge` are
   excluded because templates do write `<p class="cluster"><a class="btn">`. */
:where(p, dd, td, .prose, .prose li, .legal__section li, .reading-section__body) a:not(.button, .btn, .badge),
a.link {
    color: var(--color-accent-text);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-decoration-color: rgb(224 144 45 / 40%);
    text-underline-offset: 0.22em;
    transition: color var(--transition-fast), text-decoration-color var(--transition-fast);
}

:where(p, dd, td, .prose, .prose li, .legal__section li, .reading-section__body) a:not(.button, .btn, .badge):hover,
a.link:hover {
    color: var(--color-gold-bright);
    text-decoration-color: currentcolor;
}

::selection {
    color: var(--color-text);
    background: rgb(200 145 76 / 32%);
}

::placeholder {
    color: #858390;
    opacity: 1;
}

/* One focus ring for the whole system, gold, offset so it never sits on the
   control's own border. `:focus-visible` and not `:focus`, so a mouse click on
   a button does not leave a ring behind. */
:focus-visible {
    border-radius: var(--radius-sm);
    outline: 2px solid var(--color-gold-light);
    outline-offset: 3px;
}

/* =============================================================================
   Layout primitives

   Four of them, and they compose: `.container` sets the measure, `.section`
   sets the vertical rhythm, `.stack` spaces siblings, `.cluster` lays things
   out in a row that wraps. A page that needs a fifth almost always wants a
   component instead.
   ============================================================================= */

.container {
    width: min(100% - 2 * var(--space-5), var(--content));
    margin-inline: auto;
}

.container--narrow {
    width: min(100% - 2 * var(--space-5), var(--content-narrow));
}

.container--wide {
    width: min(100% - 2 * var(--space-5), var(--content-wide));
}

.section {
    padding-block: var(--section-space, var(--space-8));
}

.section--narrow {
    width: min(100% - 2 * var(--space-5), var(--content-narrow));
    margin-inline: auto;
}

/* Owl. `--stack-space` is the knob; `.stack--tight` and `.stack--loose` are the
   two settings common enough to deserve a name. */
.stack > * + * {
    margin-block-start: var(--stack-space, var(--space-5));
}

.stack--tight {
    --stack-space: var(--space-3);
}

.stack--loose {
    --stack-space: var(--space-7);
}

.cluster {
    display: flex;
    flex-wrap: wrap;
    gap: var(--cluster-space, var(--space-3) var(--space-4));
    align-items: center;
}

.cluster--center {
    justify-content: center;
}

.cluster--end {
    justify-content: flex-end;
}

.cluster--between {
    justify-content: space-between;
}

/* =============================================================================
   Utilities
   ============================================================================= */

.measure {
    max-width: 62ch;
}

.text-center,
.center {
    text-align: center;
}

/* `.center` also centres a block that has a width, which is how the mockup's
   centred ledes are written. Both meanings are wanted and they do not fight. */
.center {
    margin-inline: auto;
}

.lead {
    font-size: var(--text-lg);
    line-height: 1.7;
    color: var(--color-text-soft);
}

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

.desktop-break {
    display: inline;
}

.skip-link {
    position: fixed;
    z-index: 100;
    inset-block-start: 12px;
    inset-inline-start: 12px;
    padding: 10px 16px;
    color: var(--color-text-inverse);
    background: var(--color-gold-light);
    border-radius: var(--radius-sm);
    transform: translateY(-160%);
    transition: transform var(--transition-fast);
}

.skip-link:focus {
    transform: translateY(0);
}

/* =============================================================================
   Motion

   One entrance animation, staggered by an inline `--delay` custom property.
   That attribute is the reason `style-src-attr 'unsafe-inline'` exists in the
   CSP: it carries a variable and nothing else.
   ============================================================================= */

@keyframes rise {
    from {
        opacity: 0;
        transform: translateY(14px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

.reveal {
    animation: rise 800ms var(--ease) backwards;
    animation-delay: var(--delay, 0ms);
}

/* The delay has to go as well as the duration: `.reveal` fills backwards, so a
   stagger left in place would hold content invisible for half a second and
   then snap it in — the opposite of what was asked for. */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    *,
    *::before,
    *::after {
        scroll-behavior: auto !important;
        animation-delay: 0ms !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}
