/* ==========================================================================
   smooth-extras.css — the always-on layer

   Repairs to SmoothCSS v0.0.5beta, plus three blocks needed on every page.
   Every fix below has a measured before/after. Anything that changes how a
   thing *looks* rather than how it behaves lives in smooth-extras-plus.css.

     <link rel="stylesheet" href="smooth.css">
     <link rel="stylesheet" href="smooth-extras.css">
     <style> @layer tokens { :root { --color-light-primary: … } } </style>
     <script src="smooth-extras.js" defer></script>

   The script is optional and only enhances: dropdowns close on an outside
   tap, dismissible alerts get their button built, and the CSS tab group
   becomes a real ARIA widget.

   Layers: this file reuses SmoothCSS's own names, so fixes in `skins` beat
   the `global` and `layouts` rules they correct, while SmoothCSS utilities
   still beat everything here. Put project CSS in the same layers, loaded
   after — `tokens` for theme values, `skins` for your own blocks. Unlayered
   CSS beats every layer including utilities, which is how a stray
   `.my-list { margin: 0 }` silently wins over `.stack-5`.

   Each fix is wrapped in :where() and so has no specificity of its own — the
   layer is what makes it win. That way your own `.my-list { padding: … }`
   still beats it. A fix written at full specificity would outrank ordinary
   project CSS, which is worse than the bug. The blocks (.menu, .skip-link,
   .table-scroll) are normal skins and do carry specificity.

   Contents
     1. Tokens — only what this file uses
     2. Fixes — spacing leaks
     3. Fixes — links and controls (dates, multi-select, choice rows)
     4. Fixes — dialog
     5. Blocks — skip-link, menu, table-scroll, badge
     6. Utilities — eyebrow, meta
   ========================================================================== */

/* 1. Tokens -----------------------------------------------------------------
   Only what this file itself uses. The status tones, hover washes and control
   height that the plus-file blocks need live at the top of that file instead —
   copy them across along with your first block. */
@layer tokens {
  :root {
    /* Motion, for the .menu chevron. The value already carries its easing —
       writing `transition: opacity var(--transition-fast) ease` makes the
       whole declaration invalid at computed-value time. */
    --ease: cubic-bezier(0.2, 0, 0, 1);
    --transition-fast: 120ms var(--ease);

    /* One height for every single-line control, in rem rather than em so the
       box never changes with the text size — a 14px field then still lines up
       with a 16px one. 2.5rem sits within half a pixel of a SmoothCSS .button
       (40.4px), so fields and buttons align in a row. */
    --control-h: 2.5rem;

    /* Select chevron for engines without `appearance: base-select`. A
       data-URI background can't resolve currentColor, so the stroke is baked
       in and the whole image is swapped per scheme. The two values match the
       default --color-*-on-surface-variant; retune them if your theme is far
       from neutral grey. */
    --select-chevron-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='%23525252' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    --select-chevron-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none' stroke='%23c7c7c7' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' viewBox='0 0 24 24'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    --select-chevron: var(--select-chevron-light);
  }
  @media (prefers-color-scheme: dark) {
    :root {
      --select-chevron: var(--select-chevron-dark);
    }
  }
}

@layer skins {
  /* 2. Fixes — spacing leaks ------------------------------------------------ */

  /* --stack-space drives .stack, `form > * + *`, `dialog > * + *` and `hr`
     alike, and it inherits. Set it high in the tree for section rhythm —
     `<main class="stack-8-9">` — and every form and dialog inside inherits it
     too. Restores each to the default SmoothCSS falls back to. Use the stack-N
     utilities instead of --stack-space and you never need this. */
  form {
    --stack-space: 1.25em;
  }
  dialog {
    --stack-space: 1em;
  }

  /* `details[open] summary` (global) adds 0.5em below the summary. Right for a
     prose disclosure, wrong for a menu or a nav — the header bar grows while
     the menu is open. Opt out by giving the <details> a class; a bare
     <details> keeps the margin. */
  :where(details[class][open]) > summary {
    margin-block-end: 0;
  }

  /* A list carrying a layout class keeps the UA's 40px inline padding, so
     `<ul class="grid-auto">` is never actually full width. SmoothCSS only
     resets `ul:not([class])` and `[role=list]`. Add role="list" for the
     semantics; this makes the geometry right either way. */
  :where(ul, ol):where(
      .cluster,
      .repel,
      .switcher,
      .center,
      .grid-auto,
      .stack,
      [class*='stack-']
    ) {
    margin-block: 0;
    padding-inline-start: 0;
    list-style: none;
  }

  /* Global caps every <li> at --max-content-inline-size (70ch) for readability.
     Right for prose, wrong the moment the list item is a layout: its border,
     background or divider stops two thirds of the way across the row. Any
     classed list opts out, the same way SmoothCSS only styles `ul:not([class])`
     in the first place. */
  :where(ul[class], ol[class]) > li {
    max-inline-size: none;
  }

  /* 3. Fixes — links and controls -------------------------------------------- */

  /* smooth.css has `a:hover { text-decoration: none }` — hovering a link makes
     its underline vanish, which is backwards: the underline is the affordance.
     Keep it present and let it firm up instead.

     Wrapped in :where() on purpose. Being in `skins` is what beats smooth.css's
     global rule, so the selector needs no specificity of its own — and at zero
     specificity any component that styles a bare <a> (a menu, a nav, a
     breadcrumb, pagination) still wins, instead of getting stray underlines on
     hover. */
  :where(a:not([class])) {
    text-decoration-color: var(--color-outline);
    text-underline-offset: 0.2ex;
  }
  :where(a:not([class]):hover) {
    text-decoration-line: underline;
    text-decoration-color: currentColor;
  }

  /* Form controls end up three different heights: SmoothCSS gives them the
     same padding and border but no shared line-height, so each falls back to
     its own intrinsic box. Measured in Chromium: text and email 37px, date
     39px, select 40.4px — and in Safari the date field is taller again,
     because its inner value box carries a height no line-height reaches.

     So state the height outright rather than deriving it. A single-line field
     takes --control-h and centres its text natively; a textarea is multi-line
     and a [multiple]/[size] select is a list box, so both keep their natural
     height. Borrowed from chloride, which sizes buttons from the same token. */
  :where(
      input:not(
          [type='checkbox'],
          [type='radio'],
          [type='range'],
          [type='color'],
          [type='file']
        ),
      select:not([multiple], [size])
    ) {
    block-size: var(--control-h);
  }

  /* Safari and Firefox have no `appearance: base-select`, so they keep the
     native chrome and ignore SmoothCSS's ::picker(select) styling — including
     the chevron. That is why selects look different per browser and why the
     arrow does not follow the colour scheme. Draw our own. */
  @supports not (appearance: base-select) {
    select:not([multiple], [size]) {
      appearance: none;
      padding-inline-end: 2.25em;
      background-image: var(--select-chevron);
      background-repeat: no-repeat;
      background-position: right 0.75em center;
      background-size: 1em;
    }
    [dir='rtl'] select:not([multiple], [size]) {
      background-position: left 0.75em center;
    }
  }

  /* Date and time inputs. Safari sizes these from the UA sheet: taller than
     their neighbours, value centred, and wider than the width they were
     given. `appearance: none` hands the sizing back. In :where() so an
     explicit width still wins. Only visible on a device. */
  :where(
      input[type='date'],
      input[type='time'],
      input[type='month'],
      input[type='week'],
      input[type='datetime-local']
    ) {
    appearance: none;
    inline-size: 100%;
    min-block-size: var(--control-h);
  }
  /* Safari's inner value box: centred, and carrying a margin of its own. */
  ::-webkit-date-and-time-value {
    text-align: start;
    margin: 0;
  }
  /* Keep the picker button at the far edge rather than tucked against the
     value once the field is wider than its content. */
  ::-webkit-calendar-picker-indicator {
    margin-inline-start: auto;
  }

  /* iOS collapses <select multiple> to a single-line picker and sizes it from
     the UA sheet, so a height applies only after `appearance: none`. Leave the
     field's padding alone — trimming it for the list-box case strands the
     collapsed control's text against the edge. Pad the options instead. */
  :where(select[multiple]) {
    appearance: none;
    min-block-size: var(--control-h);
  }

  /* Django's CheckboxSelectMultiple and RadioSelect emit a classless
     `<label><input> Text</label>` about 22px tall — half a thumb. Matched on
     that shape, one label alone in its wrapper, so a control that sets several
     labels side by side keeps its own sizing. */
  :where(div > label:only-child:has(> input:is([type='checkbox'], [type='radio']))) {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    min-block-size: 2.75rem;
    cursor: pointer;
  }
  :where(div > label:only-child:has(> input:is([type='checkbox'], [type='radio'])))
    > input {
    inline-size: 1.15rem;
    block-size: 1.15rem;
    flex: none;
  }

  /* 4. Fixes — dialog -------------------------------------------------------- */

  /* Size. SmoothCSS sets no max, so a dialog grows until it hits the UA's own
     `calc(100% - 12px)` — not an overflow, but a near-full-screen sheet where
     you wanted a dialog. Measured in a 998×1308 viewport: 676×1143 without
     these caps, 518×1001 with. This is why the demos used
     `<dialog class="wrapper-5">` as a width cap.

     Position. Owning `position: fixed; inset: 0` rather than leaning on the
     UA's `:modal` rule also fixes an exit-transition jump in Safari and
     Firefox, which drop the dialog from the top layer the instant close()
     runs. Reported by chloride; does not reproduce in Chromium.

     Trade-off: a non-modal show() dialog no longer sits in the flow. If you
     need one: `dialog.inline { position: static; margin: 0 }`. The placement
     margins live in @layer overrides at the foot of this file. */
  dialog {
    position: fixed;
    inset: 0;
    max-inline-size: min(
      var(--dialog-max-inline-size, var(--container-6)),
      calc(100vw - 2 * var(--gutter))
    );
    max-block-size: min(
      var(--dialog-max-block-size, 85dvh),
      calc(100dvh - 2 * var(--gutter))
    );
    overflow: auto;
  }

  /* 5. Blocks ------------------------------------------------------------- */

  /* Skip link: the first focusable element on the page, visible only on focus.
       <a class="skip-link" href="#main">Hopp til innhaldet</a> */
  .skip-link {
    position: absolute;
    inset-block-start: var(--space-4);
    inset-inline-start: var(--space-4);
    z-index: 100;
    padding: var(--space-2) var(--space-4);
    background: var(--color-primary);
    color: var(--color-on-primary);
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
    border-radius: var(--border-radius-2);
  }
  .skip-link:not(:focus) {
    border: 0;
    clip-path: inset(50%);
    block-size: 1px;
    inline-size: 1px;
    margin: 0;
    overflow: hidden;
    padding: 0;
    white-space: nowrap;
  }

  /* Dropdown menu. Replaces <smooth-dropdown>, which relies on CSS anchor
     positioning and is unreliable in iOS Safari. <details> works everywhere
     with no JS; only closing on an outside tap needs script (see below).

       <details class="menu">
         <summary class="button dropdown">Handlingar</summary>
         <div>
           <p>Gruppetittel</p>
           <a href="…">Lenkje</a>
           <button type="button">Handling</button>
           <hr>
           <button type="button" class="color-error">Slett</button>
         </div>
       </details>

     Add .menu-start to align the panel to the leading edge. Knobs:
     --menu-min-inline-size, --menu-z.

     Closing on an outside tap needs script — <details> has no notion of
     "outside". smooth-extras.js does it, for .nav-disclosure too, and closes
     on Escape with focus handed back to the summary. */
  .menu {
    position: relative;
    display: inline-block;
  }
  .menu > summary {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    user-select: none;
  }
  .menu > summary::-webkit-details-marker {
    display: none;
  }
  /* Chevron drawn with borders: follows currentColor, needs no asset */
  .menu > summary::after {
    content: '';
    inline-size: 0.45em;
    block-size: 0.45em;
    border-inline-end: 1.5px solid currentColor;
    border-block-end: 1.5px solid currentColor;
    transform: translateY(-25%) rotate(45deg);
    transition: transform var(--transition-fast);
  }
  .menu[open] > summary::after {
    transform: translateY(25%) rotate(225deg);
  }
  .menu > div {
    position: absolute;
    inset-block-start: calc(100% + var(--space-1));
    inset-inline-end: 0;
    z-index: var(--menu-z, 10);
    min-inline-size: var(--menu-min-inline-size, 14rem);
    max-inline-size: calc(100vw - 2 * var(--gutter));
    padding: var(--space-1);
    background: var(--color-surface-lowest);
    border: var(--outline-variant);
    border-radius: var(--border-radius-2);
    box-shadow: var(--shadow-4);
  }
  .menu-start > div {
    inset-inline-end: auto;
    inset-inline-start: 0;
  }
  .menu > div p {
    font-size: var(--step-1);
    font-weight: var(--font-weight-body-emphasis);
    color: var(--color-on-surface-variant);
    padding: var(--space-2) var(--space-3) 0;
    max-inline-size: none;
  }
  .menu > div :is(a, button) {
    display: block;
    inline-size: 100%;
    padding: var(--space-2) var(--space-3);
    border: 0;
    border-radius: var(--border-radius-1);
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: start;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
  }
  .menu > div :is(a, button):hover,
  .menu > div :is(a, button):focus-visible {
    background: var(--color-surface-mid);
    outline: none;
  }
  .menu > div hr {
    margin-block: var(--space-1);
  }
  @media (prefers-reduced-motion: reduce) {
    .menu > summary::after {
      transition: none;
    }
  }

  /* A panel pinned to one edge of its trigger runs off the screen as soon as
     the trigger sits near the opposite edge — a .menu-start button on the
     right, a .menu button on the left. Absolute positioning cannot know that.
     Where anchor positioning is available, hand the panel to the viewport and
     let it flip its inline alignment instead. anchor-scope keeps each menu's
     anchor to its own subtree, so a page can hold as many as it likes; without
     it every menu would resolve to the last one on the page. Browsers without
     the feature keep the plain absolute panel above. */
  @supports (position-try-fallbacks: flip-inline) and (anchor-scope: --menu) {
    .menu {
      anchor-scope: --menu;
    }
    .menu > summary {
      anchor-name: --menu;
    }
    .menu > div {
      position: fixed;
      position-anchor: --menu;
      inset: auto;
      inset-block-start: calc(anchor(bottom) + var(--space-1));
      inset-inline-end: anchor(right);
      position-try-fallbacks: flip-inline, flip-block;
    }
    .menu-start > div {
      inset-inline-end: auto;
      inset-inline-start: anchor(left);
    }
  }

  /* SmoothCSS has no strategy for wide tables on narrow screens — the columns
     squash cell by cell until the table is unreadable. Wrap it:
       <div class="table-scroll" role="region" aria-label="…">
         <table>…</table>
       </div>

     Name the region: the role and the label are what a screen reader
     announces on the way in. What is deliberately NOT here is tabindex="0",
     which the usual advice adds to make the region keyboard-scrollable.

     Chrome and Firefox already focus a scroll container without it — Chrome
     only when the container holds no focusable children, on the reasoning
     that a keyboard user reaches the content by tabbing through them anyway.
     Safari does neither, and has an open WebKit bug for it. Adding the
     tabindex to close that one gap costs everyone else: Safari focuses the
     nearest focusable ancestor when you click or tap inside an element, so
     every pointer user tapping a button in a row gets the focus ring around
     the whole table — and on a phone, where a wide table overflows by
     definition, that is every user.

     So the gap is left where it belongs, with the browser. Measured in
     Safari 18 on iOS: with the tabindex, a tap on a row action rings the
     whole table; without it, nothing.

     Geometry only — nothing here touches how the table looks; see
     .table-quiet in the plus file for that. */
  .table-scroll {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    /* `overflow-x: auto` alone does NOT stop the table's min-content from
       propagating up the tree — that shortcut only applies to flex and grid
       items, and `max-inline-size: 100%` is ignored during intrinsic sizing.
       Without containment the wide table pushes every ancestor out and the
       page itself scrolls sideways instead of the wrapper. `contain:
       inline-size` makes the box size itself as if it had no contents, which
       is exactly the promise a scroll container should make. */
    contain: inline-size;
  }
  /* This is the scroll mechanism, not styling: sizing the table to max-content
     is what makes it exceed the wrapper and therefore scroll. It also stops
     cells wrapping as a side effect, so no `white-space` rule is needed. */
  .table-scroll > table {
    min-inline-size: max-content;
  }

  /* A badge that wraps to two lines stops looking like a badge. */
  .badge {
    white-space: nowrap;
  }
}

/* This one has to live in `components`: SmoothCSS declares `.button.dropdown`
   there, and components beats skins no matter how specific the selector. */
@layer components {
  /* A button is one action and its label should stay one unit. If an action
     row gets too narrow, `.switcher` can then stack whole buttons instead of
     letting one label turn into an accidental two-line button first. */
  .button {
    white-space: nowrap;
  }

  /* `.button.dropdown` replaces the button's inline padding with a fixed
     --space-3, so a dropdown trigger sits visibly narrower than the buttons
     beside it. Restore the shared --button-padding. */
  .button.dropdown {
    padding: var(--button-padding, 0.7em 1.2em);
  }
}

/* 6. Utilities ----------------------------------------------------------- */
@layer utilities {
  /* Small tracked uppercase label above a heading. Colour via --eyebrow-color. */
  .eyebrow {
    display: block;
    font-size: var(--step-1);
    font-weight: var(--font-weight-body-emphasis);
    letter-spacing: 0.1ch;
    text-transform: uppercase;
    color: var(--eyebrow-color, var(--color-on-surface-variant));
  }
  /* An eyebrow hugs the heading it introduces. Named targets rather than `*`,
     which would make the rule a candidate for every element on the page. */
  .eyebrow + :is(h1, h2, h3, h4, h5, h6, p) {
    margin-block-start: var(--space-1);
  }

  /* Secondary text: timestamps, bylines, counts, card metadata. Replaces the
     `font-size-2 + color-on-surface-variant` pair. Sets no margin on purpose —
     `.meta` and `.stack-N > * + *` are utilities of equal specificity, so a
     margin here loads later and cancels the stack's spacing. Use `margin-0`
     when you actually want that. */
  .meta {
    font-size: var(--step-2);
    color: var(--color-on-surface-variant);
    max-inline-size: none;
  }
}

/* A layer declared here lands after SmoothCSS's `utilities`, so it is the one
   place a rule can beat a utility class. Keep it to fixes that genuinely must
   win; anything else belongs in `skins` above. */
@layer overrides {
  /* A dialog is fixed and out of flow, so it must not inherit its position
     accidentally from a parent stack utility. Place it deliberately a little
     above centre; projects can tune the offset with --dialog-block-start. */
  dialog {
    margin-inline: auto;
    margin-block-start: var(--dialog-block-start, var(--space-8-9));
    margin-block-end: auto;
  }
}

/* ==========================================================================
   Writing HTML against SmoothCSS — the things that keep biting
   - Section rhythm: <div class="stack-7-8">, never --stack-space.
   - Buttons in a form: inside <div class="cluster">. Never put a layout class
     on the <form> itself.
   - Lists with a layout class: add role="list".
   - Tables: wrap in <div class="table-scroll">.
   - Dropdowns: <details class="menu">, not <smooth-dropdown>.
   - Dialogs: <form method="dialog"> with the buttons in a .cluster inside.
     No wrapper class needed any more — sizing is handled above.
   - Any class containing "wrapper" gets the full wrapper layout, so don't
     name anything .card-wrapper.
   - Project CSS goes in @layer tokens / @layer skins, never unlayered.
   - Test on the phone early.
   ========================================================================== */
