/* ============================================================
   Our Gift Circle — base styles.
   Colors come exclusively from the art-kit tokens in colors.css;
   raw hex values in components fail review (§3).
   ============================================================ */

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

html {
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
    background: var(--bg);
    color: var(--text);
}

body {
    margin: 0;
    min-height: 100dvh;
    background: var(--bg);
    color: var(--text);
    line-height: 1.5;
}

a {
    color: var(--info-text);
}

h1, h2, h3, h4 {
    color: var(--text);
    line-height: 1.2;
}

/* ---------- Focus (§5: visible indicator, --focus-ring) ---------- */

:focus-visible {
    outline: 3px solid var(--focus-ring);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

/* FocusOnNavigate (Routes.razor) parks focus on the page h1 so screen readers
   announce the new page. It gets tabindex="-1", so Tab never reaches it and no
   user action puts focus there — the ring is noise, and hiding it costs sighted
   keyboard users nothing. §5's visible-indicator rule covers keyboard-operable
   controls, which still get the ring above. Headings only; do not widen. */
h1:focus,
h1:focus-visible {
    outline: none;
}

/* Skip link for keyboard users (§5) */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100;
    padding: 0.5rem 1rem;
    background: var(--surface);
    color: var(--text);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
}

.skip-link:focus {
    left: 0.5rem;
    top: 0.5rem;
}

/* ---------- Layout ---------- */

.ogc-shell {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
}

.ogc-header {
    display: flex;
    align-items: center;
    /* Wraps so the nav can drop to its own full-width row once collapsed.
       Nothing wraps at desktop widths. */
    flex-wrap: wrap;
    gap: 1rem;
    padding: 0.75rem 1rem;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

.ogc-header .brand img,
.ogc-header .brand .brand-logo {
    height: 3rem;
    width: auto;
    display: block;
}

.ogc-nav {
    display: flex;
    gap: 0.25rem;
    flex-wrap: wrap;
    margin-left: auto;
    align-items: center;
}

/* :not(.btn) so nav links don't out-specify .btn-primary's own colors. */
.ogc-nav a:not(.btn) {
    padding: 0.5rem 0.75rem;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    text-decoration: none;
}

.ogc-nav a:not(.btn):hover {
    background: var(--surface-alt);
    color: var(--text);
}

.ogc-nav a:not(.btn).active {
    color: var(--primary-active);
    font-weight: 600;
}

/* ---------- Mobile navigation ---------- */
/* Seven links plus Sign out wrap into three ragged rows on a phone, so below
   the breakpoint they collapse behind a toggle. The nav keeps its normal
   markup and landmark either way — only its visibility changes — so nothing
   about the link order or semantics differs between the two layouts. */
.nav-toggle {
    display: none;
    margin-left: auto;
    align-items: center;
    gap: 0.4rem;
    font: inherit;
    font-weight: 600;
    padding: 0.5rem 0.9rem;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-md);
    cursor: pointer;
}

.nav-toggle:hover {
    background: var(--surface-alt);
}

@media (max-width: 52rem) {
    /* Gated on data-js: with scripting off the toggle would do nothing, so the
       links stay visible (wrapping, as before) rather than becoming
       unreachable behind a dead button. */
    :root[data-js="true"] .nav-toggle {
        display: inline-flex;
    }

    .ogc-nav {
        /* Full-width row under the brand, one link per line. */
        flex-basis: 100%;
        margin-left: 0;
        flex-direction: column;
        align-items: stretch;
        flex-wrap: nowrap;
        gap: 0.15rem;
    }

    /* display:none, not visibility/height tricks: a collapsed menu must be
       genuinely out of the tab order, or keyboard users tab into invisible
       links (§5, no traps). */
    :root[data-js="true"] .ogc-nav[data-open="false"] {
        display: none;
    }

    .ogc-nav a:not(.btn) {
        padding: 0.6rem 0.75rem;
    }

    /* Full-width tap targets for the two action controls. */
    .ogc-nav .btn,
    .ogc-nav .inline-auth-form,
    .ogc-nav .inline-auth-form .btn {
        width: 100%;
        justify-content: center;
    }
}

.inline-auth-form {
    display: inline-flex;
    margin: 0;
}

main.ogc-main {
    flex: 1;
    width: 100%;
    max-width: 80rem;
    margin: 0 auto;
    padding: 1.5rem 1rem 3rem;
}

/* Prose pages opt back down to a readable measure. Long lines are hard to
   track back to the start of, so body copy stays near 70 characters even
   though the shell is now wide enough for side-by-side app layouts. */
main.ogc-main > .prose {
    max-width: 44rem;
}

/* ---------- Card grid ---------- */
/* For pages that are a set of independent cards rather than content plus a
   panel (Profile settings). auto-fill so the count follows the width, and
   align-items: start so a short card does not stretch to a tall neighbour. */
.card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
    gap: 1rem;
    align-items: start;
    margin-top: 1rem;
}

/* A card grid built out of a list still needs its list affordances removed.
   Element-scoped so the markup only has to name the layout it wants. */
ul.card-grid,
ol.card-grid {
    list-style: none;
    padding: 0;
}

/* A card's contents stack; each part decides its own internal direction. */
.card-stack {
    display: grid;
    gap: 0.4rem;
}

/* Tags and their dismiss controls, wrapping rather than overflowing a card. */
.tag-row {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-wrap: wrap;
}

/* ---------- Two-column page layout ---------- */
/* Main content beside a secondary panel (e.g. lists next to the create form).
   One column until there is genuinely room for two, so narrow windows and
   phones stack in DOM order. align-items: start keeps the side panel its own
   height instead of stretching to match the main column. */
.page-columns {
    display: grid;
    gap: 1.5rem;
    align-items: start;
}

@media (min-width: 62rem) {
    .page-columns {
        grid-template-columns: minmax(0, 1fr) 24rem;
    }

    /* For pages where the second column is a full form rather than a narrow
       panel, so it needs real room. */
    .page-columns-split {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Cancels the stacked spacing the sections carry on their own. */
.page-columns > * > :first-child {
    margin-top: 0;
}

.ogc-footer {
    padding: 1rem;
    text-align: center;
    color: var(--text-muted);
    border-top: 1px solid var(--border);
    background: var(--surface);
}

/* ---------- Buttons ---------- */
/* Solid-fill buttons carry dark ink (--on-primary / --on-danger / --on-secondary),
   never white. An earlier note here claimed 16px semibold made white-on-coral an
   AA large-text case; it does not. WCAG large text is >=24px, or >=18.66px at
   weight 700, so these 16px/600 labels need the full 4.5:1 — which white on
   coral (3.01:1) and white on red (3.74:1) both missed. */

.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 1rem;
    font-weight: 600;
    padding: 0.55rem 1.1rem;
    border-radius: var(--radius-md);
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
}

.btn-primary {
    background: var(--primary);
    color: var(--on-primary);
}

.btn-primary:hover {
    background: var(--primary-hover);
}

.btn-primary:active {
    background: var(--primary-active);
}

.btn-confirm {
    background: var(--secondary);
    color: var(--on-secondary);
}

.btn-confirm:hover {
    background: var(--secondary-hover);
}

.btn-quiet {
    background: var(--surface);
    color: var(--text);
    border-color: var(--border-strong);
}

.btn-quiet:hover {
    background: var(--surface-alt);
}

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

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

/* ---------- An action plus the option that modifies it ---------- */
/* Keeps a control and its option visibly paired. Loose in a row of buttons, a
   bare checkbox reads as stray text and gives no clue which button it changes. */
.action-with-option {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

/* Deliberately not a .btn: it decides something rather than doing something.
   Matches button height and radius so the pair lines up, but a dashed border,
   lighter weight and secondary colour keep it subordinate to the real action. */
.option-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: 1rem;
    font-weight: 500;
    padding: 0.55rem 0.9rem;
    border: 1px dashed var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--surface);
    color: var(--text-secondary);
    cursor: pointer;
    white-space: nowrap;
}

.option-chip:hover {
    background: var(--surface-alt);
    color: var(--text);
}

/* Ticked reads as committed. The cue is deliberately weight and text colour
   rather than a tinted border or fill: against these surfaces --secondary is
   2.2:1 and --secondary-subtle only 1.14:1, so either would be a state change
   nobody could see. Weight and full-strength text carry it at 5.7:1 or better,
   and the border goes solid as a second, non-colour cue. The checkbox itself
   remains the primary indicator, so none of this is load-bearing. */
.option-chip:has(input:checked) {
    border-style: solid;
    color: var(--text);
    font-weight: 600;
}

.option-chip input {
    margin: 0;
    flex: none;
}

/* ---------- Cards ---------- */

.card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: 1rem 1.25rem;
}

/* ---------- Forms (§5: labels, visible errors) ---------- */

.form-field {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    margin-bottom: 1rem;
}

.form-field label {
    font-weight: 600;
    color: var(--text);
}

/* Not scoped to .form-field: most of the 45 .hint uses sit directly in a card
   (Profile, Circles, Family, …) and were rendering as plain body text. */
.hint {
    color: var(--text-muted);
    font-size: 0.875rem;
}

/* Element-scoped for the same reason — pages that lay controls out in a plain
   flex row (the profile birthday month/day, the invitation-id and new-log-name
   inputs) fell through to unstyled browser defaults, which read as broken in
   dark mode. Checkboxes and radios keep their native rendering. */
input:not([type="checkbox"]):not([type="radio"]),
textarea,
select {
    font: inherit;
    padding: 0.5rem 0.65rem;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
}

/* ---------- Multi-column form fields ---------- */
/* Short fields pair up once there is room, so a form with several of them
   stops being a tall single column with dead space beside it. Row gap is 0
   because .form-field already carries margin-bottom; only the column gap is
   set here, or the vertical rhythm would double. */
/* .form-host marks the <form> as the query container; .form-grid inside it
   pairs fields up based on the width the form actually has, not the width of
   the window. A viewport query gets this wrong the moment a form sits in a
   column: the window is wide, the column is not, and the fields end up
   squeezed two-across into far too little room. A form that forgets
   .form-host simply stays single-column, which is the safe direction. */
.form-host {
    container-type: inline-size;
}

.form-grid {
    display: grid;
    grid-template-columns: 1fr;
    gap: 0 1.25rem;
}

@container (min-width: 34rem) {
    .form-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* Anything that wants the whole row: long text, textareas, fieldsets. */
    .form-grid > .form-field-wide,
    .form-grid > fieldset {
        grid-column: 1 / -1;
    }
}
.field-error {
    color: var(--danger-text);
    font-size: 0.9rem;
}

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

/* ---------- Analytics consent banner ---------- */
/* Anchored bottom-centre rather than covering the viewport: declining is one
   click away and the page stays readable behind it, so nobody is coerced into
   accepting to get at the content. Visibility is driven by the [hidden]
   attribute from consent.js — see the global [hidden] rule under Utility. */
.consent-banner {
    position: fixed;
    z-index: 40;
    inset-inline: 0;
    bottom: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.75rem 1.25rem;
    max-width: 48rem;
    margin: 0 auto 1rem;
    padding: 1rem 1.25rem;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}

.consent-banner-text {
    flex: 1 1 18rem;
    margin: 0;
    font-size: 0.9375rem;
}

.consent-banner-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}

@media (prefers-reduced-motion: no-preference) {
    .consent-banner {
        animation: consent-rise 0.2s ease-out;
    }
}

@keyframes consent-rise {
    from { transform: translateY(0.5rem); opacity: 0; }
    to   { transform: none; opacity: 1; }
}

/* ---------- Status tags (§5: icon + text, never color alone) ---------- */

.tag {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.85rem;
    font-weight: 600;
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
}

.tag-warning {
    background: var(--warning-subtle);
    color: var(--warning-text);
}

.tag-success {
    background: var(--success-subtle);
    color: var(--success-text);
}

.tag-info {
    background: var(--info-subtle);
    color: var(--info-text);
}

/* Purchased-item cue (§9.1): primary-color shadow, always paired with the
   icon + text tag so the state never relies on color alone (§5). Visible
   only in the shared (viewer's) list, so surprise protection holds. */
.card-reserved {
    box-shadow: 0 0 0 2px var(--primary), var(--shadow-sm);
}

/* ---------- Toasts ---------- */
/* Bottom-right and fixed, because the page-level banners these replace sat at
   the top of the document and were usually scrolled out of view by the time an
   action finished. Informational toasts clear themselves after four seconds;
   errors stay until dismissed. */
.toast-host {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 60;
    display: grid;
    gap: 0.5rem;
    width: min(24rem, calc(100vw - 2rem));
    /* The host spans a column even when empty, so it must not swallow clicks
       on whatever is underneath. Individual toasts opt back in. */
    pointer-events: none;
}

.toast-stack {
    display: grid;
    gap: 0.5rem;
}

.toast {
    pointer-events: auto;
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.75rem 0.9rem;
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    border-left-width: 4px;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
}

/* Colour is a secondary cue only — the message itself always says what
   happened, so nothing depends on distinguishing these (§5). */
.toast-success { border-left-color: var(--success); }
.toast-info    { border-left-color: var(--info); }
.toast-error   { border-left-color: var(--danger); }

.toast-message {
    flex: 1;
    min-width: 0;
    /* Long names and pasted URLs appear in these; wrap rather than overflow. */
    overflow-wrap: anywhere;
}

.toast-dismiss {
    flex: none;
    font: inherit;
    line-height: 1;
    padding: 0.15rem 0.35rem;
    background: none;
    border: 0;
    border-radius: var(--radius-sm);
    color: var(--text-secondary);
    cursor: pointer;
}

.toast-dismiss:hover {
    background: var(--surface-alt);
    color: var(--text);
}

@media (prefers-reduced-motion: no-preference) {
    .toast {
        animation: toast-in 0.15s ease-out;
    }
}

@keyframes toast-in {
    from { transform: translateY(0.5rem); opacity: 0; }
    to   { transform: none; opacity: 1; }
}
/* ---------- Modal (confirmation dialogs) ---------- */

.modal-overlay {
    position: fixed;
    inset: 0;
    background: var(--overlay);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    z-index: 50;
}

.modal {
    max-width: 26rem;
    width: 100%;
    box-shadow: var(--shadow-lg);
}

/* ---------- Utility ---------- */

/* The UA stylesheet's [hidden] { display: none } is author-overridable, so any
   class setting an explicit display silently defeats it — .btn's inline-flex
   kept the privacy page's allow/withdraw buttons visible in both states, and
   .consent-banner's flex did the same. This is the one place !important earns
   its keep: hidden has to mean hidden, whatever else an element is styled as. */
[hidden] {
    display: none !important;
}

.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;
}

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

/* ---------- Motion (§5: honor prefers-reduced-motion) ---------- */

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