/* =============================================================================
   Eyvi.Design - base element styling
   -----------------------------------------------------------------------------
   A small reset and defaults for bare HTML elements, in token values only. No
   component styling lives here, except the buttons, the layout primitives, the
   two table patterns and the chart category classes: the shared vocabulary every
   screen is built from.
   ============================================================================= */
*,
*::before,
*::after {
    box-sizing: border-box;
}

/* The reader's own font size is respected. overscroll-behavior stops a scroll
   that reaches the end of the page becoming pull-to-refresh, which in an
   installed app would reload it mid-entry. */
html {
    -webkit-text-size-adjust: 100%;
    overscroll-behavior-y: contain;
}

/* NO WORD MAY MAKE THE PAGE WIDER THAN THE SCREEN. Addresses, paths and long
   compound words have nothing to break on and would otherwise paint out of their
   boxes and drag the whole page sideways on a phone. "anywhere" rather than
   "break-word", because only "anywhere" also lowers a word's contribution to a
   box's minimum width - which is what keeps a table cell from widening the page.
   It acts only where a word would overflow. */
body {
    margin: 0;
    background-color: var(--color-surface-page);
    color: var(--color-text);
    font-family: var(--font-family-base);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-regular);
    line-height: var(--line-height-base);
    letter-spacing: var(--letter-spacing-base);
    overflow-wrap: anywhere;
}

h1, h2, h3, h4, h5, h6 {
    margin: 0 0 var(--space-3);
    font-family: var(--font-family-heading);
    font-weight: var(--font-weight-semibold);
    line-height: var(--line-height-tight);
    letter-spacing: var(--letter-spacing-tight);
}

/* No heading breaks with one orphaned word at phone widths. */
h1, h2, h3 {
    text-wrap: balance;
}

h1 { font-size: var(--font-size-2xl); }
h2 { font-size: var(--font-size-xl); }
h3 { font-size: var(--font-size-lg); }
h4, h5, h6 { font-size: var(--font-size-base); }

p {
    margin: 0 0 var(--space-4);
}

a {
    color: var(--color-link);
    text-decoration: none;
}

a:hover,
a:focus-visible {
    color: var(--color-link-hover);
    text-decoration: underline;
}

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

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

hr {
    border: 0;
    border-top: var(--border-width) solid var(--color-border);
    margin: var(--space-5) 0;
}

/* Media never widens its container. */
img, svg, video, canvas {
    max-width: 100%;
    height: auto;
}

/* -----------------------------------------------------------------------------
   Forms and buttons
   ----------------------------------------------------------------------------- */
button,
input,
select,
textarea {
    font: inherit;
    color: inherit;
}

/* -----------------------------------------------------------------------------
   Touch behaviour
   -----------------------------------------------------------------------------
   No double-tap-to-zoom delay and no tap-highlight flash on controls: the
   pressed state below is the feedback. */
a,
button,
label,
summary,
[role="button"] {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}

/* A long press on a control does not start selecting its text. Inputs are
   untouched: selecting text there is the point. */
button,
label {
    -webkit-user-select: none;
    user-select: none;
}

/* .button styles a <label> as a button - the way to offer a visually-hidden file
   input. Appearance only. */
button,
.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: var(--layout-touch-target-min);
    padding: var(--space-2) var(--space-4);
    border: var(--border-width) solid var(--color-border-strong);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-container);
    color: var(--color-text);
    cursor: pointer;
    transition:
        background-color var(--motion-duration-fast) var(--motion-easing-standard),
        border-color var(--motion-duration-fast) var(--motion-easing-standard);
}

/* Hover and press are a translucent layer over the variant's own background, so
   one rule serves every variant. */
button:hover:not(:disabled),
.button:hover:not(:disabled) {
    background-image: linear-gradient(var(--color-state-hover), var(--color-state-hover));
    border-color: var(--color-border-outline);
}

button:active:not(:disabled),
.button:active:not(:disabled) {
    background-image: linear-gradient(var(--color-state-pressed), var(--color-state-pressed));
}

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

/* The pale marine fill carries DARK text (11.88:1); white would be 1.28:1. */
button.primary,
.button.primary {
    background-color: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-on-primary);
    font-weight: var(--font-weight-medium);
}

button.secondary,
.button.secondary {
    background-color: var(--color-secondary);
    border-color: var(--color-secondary);
    color: var(--color-on-secondary);
    font-weight: var(--font-weight-medium);
}

/* Destructive, as an outline: states the intent without shouting on every row. */
button.danger,
.button.danger {
    background-color: var(--color-danger-subtle);
    border-color: var(--color-danger);
    color: var(--color-danger-text);
    font-weight: var(--font-weight-medium);
}

/* Destructive, filled: a confirmation's final button. Uses the darkened danger
   tone, since neither white nor dark text clears AA on the pale one. */
button.danger-filled,
.button.danger-filled {
    background-color: var(--color-danger-strong);
    border-color: var(--color-danger-strong);
    color: var(--color-white);
    font-weight: var(--font-weight-medium);
}

/* An icon-only or inline control that should not read as a box. */
button.quiet,
.button.quiet {
    background-color: transparent;
    border-color: transparent;
    color: var(--color-text-secondary);
}

button.quiet:hover:not(:disabled),
.button.quiet:hover:not(:disabled) {
    border-color: transparent;
    color: var(--color-text);
}

/* Inputs have their own surface, recessed in the dark theme, and are full width
   by default. Textareas resize vertically only. */
input,
select,
textarea {
    min-height: var(--layout-touch-target-min);
    padding: var(--space-2) var(--space-3);
    border: var(--border-width) solid var(--color-border-strong);
    border-radius: var(--radius-md);
    background-color: var(--color-surface-input);
    color: var(--color-text);
    max-width: 100%;
}

textarea {
    resize: vertical;
    min-height: calc(var(--layout-touch-target-min) * 2);
}

input:hover:not(:disabled),
select:hover:not(:disabled),
textarea:hover:not(:disabled) {
    border-color: var(--color-text-muted);
}

input:disabled,
select:disabled,
textarea:disabled {
    background-color: var(--color-surface-inset);
    opacity: 0.7;
    cursor: not-allowed;
}

::placeholder {
    color: var(--color-text-muted);
    opacity: 1;
}

/* Native checkboxes, radios and range thumbs in the accent colour, both themes. */
input[type="checkbox"],
input[type="radio"],
input[type="range"] {
    accent-color: var(--color-accent);
    min-height: 0;
}

input[type="checkbox"],
input[type="radio"] {
    width: 1.15rem;
    height: 1.15rem;
    padding: 0;
}

label {
    display: inline-block;
    margin-bottom: var(--space-1);
    font-weight: var(--font-weight-medium);
}

fieldset {
    margin: 0 0 var(--space-5);
    padding: 0;
    border: 0;
}

legend {
    padding: 0;
    font-weight: var(--font-weight-semibold);
}

/* One focus treatment, keyboard-only, drawn as a ring rather than an outline.
   Elements focused programmatically (tabindex="-1") show none. */
:focus-visible:not([tabindex="-1"]) {
    outline: none;
    box-shadow: var(--shadow-focus);
    border-radius: var(--radius-sm);
}

[tabindex="-1"]:focus {
    outline: none;
}

/* Blazor's built-in validation classes, mapped onto the tokens. The message uses
   the text tone: --color-danger is a fill, 3.73:1 as text on white. */
.valid.modified:not([type=checkbox]) {
    border-color: var(--color-success);
}

.invalid {
    border-color: var(--color-danger);
}

.validation-message {
    display: block;
    margin-top: var(--space-1);
    color: var(--color-danger-text);
    font-size: var(--font-size-sm);
}

/* -----------------------------------------------------------------------------
   Tables
   -----------------------------------------------------------------------------
   Two patterns:

   1. .table-scroll - a table that stays a table and scrolls sideways in its own
      box. For a few short columns, where comparing rows matters most.

   2. .data-table - below 48rem each row becomes a card, each cell labelled from
      its data-label attribute. For wide record lists. Every cell but an actions
      cell needs data-label. Changing a table's display drops its semantics, so
      the markup carries role="table" / role="row" / role="cell".

   Cells align to the top: side by side, boxes start at the same height.
   ----------------------------------------------------------------------------- */
table {
    width: 100%;
    border-collapse: collapse;
}

th, td {
    padding: var(--space-3);
    border-bottom: var(--border-width) solid var(--color-border);
    text-align: left;
    vertical-align: top;
}

thead th {
    background-color: var(--color-surface-table-header);
}

th {
    font-weight: var(--font-weight-semibold);
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm);
    letter-spacing: var(--letter-spacing-wide);
    text-transform: uppercase;
}

.table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

@media (max-width: 48rem) {
    .data-table thead {
        display: none;
    }

    .data-table,
    .data-table tbody,
    .data-table tr,
    .data-table td {
        display: block;
        width: 100%;
    }

    .data-table tr {
        margin-bottom: var(--space-4);
        padding: var(--space-2) var(--space-4);
        border: var(--border-width) solid var(--color-border);
        border-radius: var(--radius-lg);
        background-color: var(--color-surface-container);
    }

    .data-table td {
        display: grid;
        grid-template-columns: minmax(6rem, 40%) 1fr;
        gap: var(--space-3);
        align-items: baseline;
        padding: var(--space-2) 0;
        border-bottom: var(--border-width) solid var(--color-border);
    }

    .data-table td:last-child {
        border-bottom: 0;
    }

    .data-table td::before {
        content: attr(data-label);
        font-size: var(--font-size-xs);
        font-weight: var(--font-weight-semibold);
        color: var(--color-text-muted);
        letter-spacing: var(--letter-spacing-wide);
        text-transform: uppercase;
    }

    .data-table td:not([data-label]) {
        grid-template-columns: 1fr;
    }

    .data-table td:not([data-label])::before {
        content: none;
    }
}

/* -----------------------------------------------------------------------------
   Layout primitives
   -----------------------------------------------------------------------------
   Spacing comes from a layout rule rather than margins on individual elements.
   Boxes side by side start at the same height.
   ----------------------------------------------------------------------------- */

/* Vertical rhythm; --stack-gap per instance. */
.stack {
    display: flex;
    flex-direction: column;
    gap: var(--stack-gap, var(--space-5));
}

/* A horizontal group that wraps instead of overflowing: toolbars, action rows. */
.cluster {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--cluster-gap, var(--space-3));
}

/* Cards that reflow to one column with no media query. The minimum is clamped to
   100% so a narrow phone never overflows; --grid-min per instance. */
.grid-auto {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(var(--grid-min, 18rem), 100%), 1fr));
    gap: var(--grid-gap, var(--space-5));
}

/* -----------------------------------------------------------------------------
   Chart category colours
   -----------------------------------------------------------------------------
   GLOBAL on purpose. Several views of one breakdown - bars, a pie, a heatmap -
   draw the same categories in the same order, and a reader matches them by
   colour, so a category must be one colour in all of them. Scoped CSS cannot be
   shared between components; these classes can.

   Each class sets one custom property, --chart-colour, rather than a background,
   so a chart applies it to whatever the shape is - a bar, a slice, a legend
   swatch. Eight at most; see tokens.css. An application with a second
   categorical set declares its own --app- tokens and classes setting the same
   property.
   ----------------------------------------------------------------------------- */
.chart-cat-1 { --chart-colour: var(--color-category-1); }
.chart-cat-2 { --chart-colour: var(--color-category-2); }
.chart-cat-3 { --chart-colour: var(--color-category-3); }
.chart-cat-4 { --chart-colour: var(--color-category-4); }
.chart-cat-5 { --chart-colour: var(--color-category-5); }
.chart-cat-6 { --chart-colour: var(--color-category-6); }
.chart-cat-7 { --chart-colour: var(--color-category-7); }
.chart-cat-8 { --chart-colour: var(--color-category-8); }

/* -----------------------------------------------------------------------------
   Accessibility
   ----------------------------------------------------------------------------- */

/* Off screen, but read aloud. Pinned left so a hidden box can never sit past the
   right edge and widen the page; table-layout: fixed so a hidden table (a chart's
   figures) takes its 1px width instead of its content's. */
.visually-hidden {
    position: absolute;
    left: 0;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
    table-layout: fixed;
}

/* A hidden table's caption may wrap, or its width would still widen the page. */
.visually-hidden caption {
    white-space: normal;
}

/* Reduced motion: animations and transitions collapse to an instant. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}
