/* ============================================================
   goen — base layer.

   The tokens a page reads, the element defaults every page starts from,
   and the shared primitives more than one surface still wears. app.css
   (the storefront) and admin.css (the back office) are each linked after
   it, so a value set in both places is settled by source order and theirs
   wins.

   A custom property both surfaces read is declared here, once.
   ============================================================ */

:root {
  --col-gap: clamp(16px, 1.7vw, 24px);
  /* The brand names are the accent under the spelling the shared
     primitives were written against. They follow --accent rather than
     repeating it, so the accent moves in one place. */
  --brand: var(--accent);
  --brand-muted: var(--accent-muted);
  --brand-faint: var(--accent-faint);

  --font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;

  /* The UI type ladder. The editorial step is the one size above it that
     a heading still reaches for. */
  --fs-11: 0.6875rem;
  --fs-12: 0.750rem;
  --fs-13: 0.8125rem;
  --fs-14: 0.875rem;
  --fs-16: 1.000rem;
  --fs-18: 1.125rem;
  --fs-ed-20: 1.250rem;

  --lh-snug: 1.35;
  --lh-normal: 1.5;
  --lh-read: 1.75;

  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;

  --track-tight: -0.02em;

  /* Stacking. Three levels, because a page that needs a fourth has a
     layering problem a number will not fix. */
  --z-base: 0;
  --z-dropdown: 60;
  --z-toast: 200;

  /* A radius that is always a circle, whatever --radius is. */
  --r-full: 999px;

  --container: 1280px;

  --ease-standard: cubic-bezier(0.2, 0, 0, 1);
  --dur-base: 120ms;
  --dur-slow: 200ms;

  /* Position and size, where the easing has to read as a sheet being set down
     rather than a colour changing. */
  --ease-move: cubic-bezier(0.32, 0.72, 0, 1);
  --dur-move: 280ms;

  /* Form controls, scrollbars and the canvas a page has not painted all
     follow this, and goen draws one ground. */
  color-scheme: light;
}

/* ---------- Element defaults ---------- */

/* The root stays at the browser's own size so a rem is the reader's
   setting; body text sizes itself from the ladder above. */
html,
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Every focusable thing has a ring before any component asks for one. */
*:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* ---------- Button ---------- */

.ui-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  appearance: none; -webkit-appearance: none;
  font-family: var(--font-sans); font-weight: var(--fw-medium); line-height: 1;
  border-radius: var(--r-sm); border: 1px solid transparent;
  cursor: pointer; white-space: nowrap; text-decoration: none;
  padding: 8px 14px; font-size: var(--fs-13);
  transition: background var(--dur-base) var(--ease-standard), border-color var(--dur-base) var(--ease-standard), box-shadow var(--dur-base) var(--ease-standard), color var(--dur-base) var(--ease-standard);
}
.ui-btn:disabled,
.ui-btn[aria-disabled="true"],
.ui-btn[disabled] { opacity: 0.4; cursor: not-allowed; pointer-events: none; }

.ui-btn--sm { padding: 6px 10px; font-size: var(--fs-13); }
.ui-btn--icon { padding: 8px; }            /* square icon-only */
.ui-btn--icon.ui-btn--sm { padding: 6px; }

.ui-btn--primary { background: var(--accent); color: var(--on-accent); box-shadow: var(--sh-xs); }
.ui-btn--primary:hover  { background: var(--accent-deep); }
.ui-btn--primary:active { background: var(--accent-deep); }

/* Soft: a blue tint and no outline. The transparent border is what forced
   colours draws as its edge. */
.ui-btn--secondary { background: var(--accent-faint); color: var(--accent-deep); border-color: transparent; }
.ui-btn--secondary:hover  { background: var(--accent-muted); }
.ui-btn--secondary:active { background: var(--accent-muted); }

.ui-btn--ghost { background: transparent; color: var(--fg-muted); }
.ui-btn--ghost:hover  { background: var(--overlay); color: var(--fg); }
.ui-btn--ghost:active { background: var(--panel); }

.ui-btn svg { width: 16px; height: 16px; flex: none; }
.ui-btn--sm svg { width: 14px; height: 14px; }

/* ---------- Badge ---------- */

.ui-badge {
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
  font-family: var(--font-sans); font-size: var(--fs-11); font-weight: var(--fw-medium);
  line-height: 1; padding: 3px 8px; border-radius: var(--r-full);
  background: var(--elevated); color: var(--fg-muted); border: 1px solid transparent;
}
.ui-badge--brand { background: var(--brand-muted); color: var(--accent-deep); }
.ui-badge--warn  { background: var(--warn-bg);     color: var(--warn); }

/* count pill (the cart's number) */
.ui-badge--count {
  min-width: 18px; height: 18px; padding: 0 5px; justify-content: center;
  border-radius: var(--r-full); font-size: var(--fs-11); font-variant-numeric: tabular-nums;
  background: var(--n-900); color: var(--n-0); border: none;
}

/* ---------- Form controls ---------- */

.ui-field { display: flex; flex-direction: column; gap: 6px; }
.ui-label {
  font-family: var(--font-sans); font-size: var(--fs-12); font-weight: var(--fw-medium);
  color: var(--fg-muted);
}
.ui-error-text { font-size: var(--fs-12); color: var(--error); }

.ui-input, .ui-textarea {
  font-family: var(--font-sans); font-size: var(--fs-13); color: var(--fg);
  background: var(--n-0); border: 1px solid var(--control-boundary); border-radius: var(--r-md);
  padding: 8px 10px; width: 100%; box-sizing: border-box; outline: none;
  transition: border-color var(--dur-base) var(--ease-standard), background var(--dur-base);
}
.ui-input::placeholder, .ui-textarea::placeholder { color: var(--fg-muted); }
.ui-input:focus, .ui-textarea:focus { border-color: var(--accent); }
.ui-input:disabled, .ui-textarea:disabled { opacity: 0.45; cursor: not-allowed; }
.ui-textarea { resize: vertical; min-height: 80px; line-height: var(--lh-normal); }

.ui-input--sm { padding: 6px 8px; font-size: var(--fs-12); }

.ui-select {
  font-family: var(--font-sans); font-size: var(--fs-13); color: var(--fg);
  background-color: var(--n-0); border: 1px solid var(--control-boundary); border-radius: var(--r-md);
  padding: 8px 30px 8px 10px; width: 100%; box-sizing: border-box; outline: none; cursor: pointer;
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2366666d' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 9px center;
  transition: border-color var(--dur-base);
}
.ui-select:focus { border-color: var(--brand); }
.ui-select:disabled { opacity: 0.45; cursor: not-allowed; }

.ui-input:focus-visible, .ui-textarea:focus-visible, .ui-select:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px;
}

/* ---------- Stat line: a label, a figure with its unit and one note ----------
   No rule over a stat: the small label, the large figure and the space between stats
   already make each one a unit. Each stat is as wide as what it says.
   Space and size are rem, so they grow with the reader's text; nothing here needs a style attribute. */

.ui-statline { --stat-min: 6.5rem; --stat-gap: 2.5rem; display: flex; flex-wrap: wrap; gap: 1.25rem var(--stat-gap); max-width: 44rem; margin: 0; }
.ui-statline > div { min-width: min(100%, var(--stat-min)); }
.ui-statline dt { font-size: var(--t-12); line-height: 1.4; color: var(--fg-muted); letter-spacing: 0.02em; }
.ui-statline dd { margin: 0.125rem 0 0; font-size: var(--t-24); font-weight: var(--fw-display); line-height: 1.25; font-variant-numeric: tabular-nums lining-nums; letter-spacing: -0.01em; word-break: keep-all; overflow-wrap: break-word; }
.ui-statline dd small { font-size: var(--t-13); font-weight: 400; line-height: 1; color: var(--fg-muted); letter-spacing: 0; }
.ui-statline__pre { margin-right: 0.08em; }
.ui-statline__note { display: block; margin-top: 0.25rem; font-size: var(--t-12); font-weight: 400; line-height: 1.5; color: var(--fg-muted); letter-spacing: 0; }
.ui-statline--wide, .ui-statline--glance, .ui-statline--pairs { max-width: none; }
/* two across wherever two fit, so four read as two rows of two */
.ui-statline--pairs > div { flex: 1 1 calc(50% - var(--stat-gap) / 2); }
/* the glance's stats carry a chart drawn at 100% of their width, so they share the row
   rather than take their content's width */
.ui-statline--glance > div { flex: 1 1 var(--stat-min); }
.ui-statline--wide > div { flex: 1 1 var(--stat-min); }
.ui-statline--s { --stat-min: 7rem; --stat-gap: 1.75rem; row-gap: 0.25rem; margin-top: var(--s-2); }
.ui-statline.ui-statline--s dd { font-size: var(--t-16); font-weight: 400; letter-spacing: 0; }
.ui-statline--glance dt a { color: inherit; text-decoration: underline; text-decoration-color: var(--border-strong); text-underline-offset: 0.2em; }
.ui-statline--glance dt a:hover { text-decoration-color: currentcolor; }
/* four stats read as two rows of two, never three and one; when two of --stat-min do not fit
   (large text on a phone), one stat per row */
@media (max-width: 700px) { .ui-statline:has(> :nth-child(4):last-child) > div { flex-basis: calc(50% - var(--stat-gap) / 2); } }
/* a phone: the figure steps down, a figure never breaks, and three short stats keep one row */
@media (max-width: 420px) { .ui-statline { --stat-min: 5rem; --stat-gap: 1.75rem; } .ui-statline dd { font-size: var(--t-20); overflow-wrap: normal; } .ui-statline.ui-statline--glance { --stat-min: 8rem; } }

/* ---------- Breadcrumbs ---------- */

.ui-crumbs { display: flex; align-items: center; gap: 8px; font-size: var(--fs-14); color: var(--n-500); flex-wrap: wrap; font-variant-numeric: tabular-nums; }
.ui-crumbs a { color: var(--fg-muted); text-decoration: none; }
.ui-crumbs__sep { color: var(--n-300); }
.ui-crumbs__current { color: var(--n-900); }

/* ---------- Navigation item (header and back-office rail) ---------- */

.ui-navitem {
  display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: var(--r-sm);
  font-family: var(--font-sans); font-size: var(--fs-13); color: var(--fg-muted);
  text-decoration: none; cursor: pointer; transition: background var(--dur-base), color var(--dur-base);
}
.ui-navitem:hover { background: var(--overlay); color: var(--fg); }
.ui-navitem[aria-current="page"], .ui-navitem.is-active { background: var(--n-100); color: var(--fg); }
.ui-navitem svg { width: 17px; height: 17px; color: var(--fg-muted); flex: none; }
.ui-navitem.is-active svg { color: var(--n-900); }

/* ---------- Inline alert ---------- */

.ui-alert { display: flex; gap: 10px; padding: 10px 12px; border-radius: var(--r-sm); font-family: var(--font-sans); font-size: var(--fs-13); line-height: var(--lh-normal); }
.ui-alert svg { width: 16px; height: 16px; flex: none; margin-top: 1px; }
.ui-alert--info  { background: var(--info-bg);  color: var(--info); }
.ui-alert--error { background: var(--error-bg); color: #991b1b; }
.ui-alert strong { font-weight: var(--fw-semibold); }
.ui-alert__body { color: var(--fg-muted); }

/* ---------- Empty state ---------- */

.ui-empty { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 12px; padding: 48px 24px; }
.ui-empty__title { font-family: var(--font-display); font-size: var(--fs-18); font-weight: var(--fw-semibold); color: var(--fg-muted); margin: 0; }
.ui-empty__desc { font-family: var(--font-sans); font-size: var(--fs-13); color: var(--fg-muted); margin: 0; max-width: 320px; line-height: var(--lh-normal); }

/* ---------- Table ---------- */

.ui-table { width: 100%; border-collapse: collapse; font-family: var(--font-sans); font-size: var(--fs-13); }
.ui-table thead th {
  text-align: left; font-family: var(--font-sans); font-size: var(--fs-11); font-weight: var(--fw-medium);
  color: var(--fg-muted); letter-spacing: 0.04em; text-transform: uppercase;
  padding: 8px 12px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
.ui-table tbody td { padding: 9px 12px; border-bottom: 1px solid var(--border-faint); color: var(--fg-muted); vertical-align: middle; height: 32px; box-sizing: border-box; }
.ui-table tbody tr { transition: background var(--dur-base); }
.ui-table tbody tr:hover { background: var(--overlay); }

/* ---------- Description list ---------- */

/* A term and its value: the term's column and colour part the rows, so no rule runs between them. */
.ui-dl { display: flex; flex-direction: column; gap: 8px; }
.ui-dl__row { display: grid; grid-template-columns: 180px 1fr; gap: 16px; }
.ui-dl__term { font-size: var(--fs-13); color: var(--fg-muted); }
.ui-dl__desc { margin: 0; font-size: var(--fs-13); color: var(--fg); }

/* ---------- Page heading ---------- */

.ui-page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 20px; flex-wrap: wrap; padding-bottom: 24px; }
.ui-page-head__eyebrow { font-family: var(--font-sans); font-size: var(--fs-11); letter-spacing: 0.08em; text-transform: uppercase; color: var(--fg-muted); margin: 0 0 6px; white-space: nowrap; }
.ui-page-head__title { font-family: var(--font-display); font-size: var(--fs-ed-30); font-weight: var(--fw-medium); letter-spacing: var(--track-tight); color: var(--fg); margin: 0; }
.ui-page-head__sub { font-size: var(--fs-13); color: var(--fg-muted); margin: 6px 0 0; max-width: 60ch; }
.ui-page-head__actions { display: flex; align-items: center; gap: 8px; flex: none; }

/* ---------- Filter bar ---------- */

.ui-filterbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ui-filter { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-sans); font-size: var(--fs-12); font-weight: var(--fw-medium); color: var(--fg-muted); background: var(--elevated); border: 1px solid var(--border); border-radius: var(--r-sm); padding: 5px 10px; cursor: pointer; transition: color var(--dur-base), border-color var(--dur-base), background var(--dur-base); }
.ui-filter:hover { color: var(--fg); border-color: var(--border-strong); }
.ui-filter__count { font-variant-numeric: tabular-nums; font-size: var(--fs-12); color: var(--fg-muted); }
.ui-filterbar__clear { font-family: inherit; font-size: var(--fs-12); color: var(--fg-muted); background: none; border: none; cursor: pointer; padding: 4px; }
.ui-filterbar__clear:hover { color: var(--fg-muted); }

/* ---------- Cross-cutting states ----------

   These come after the components they touch, which is what lets one
   rule answer for all of them. Keep them last.
   ------------------------------------------------------------------ */

.ui-navitem:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* The browser's own verdict on a control the reader has touched, and
   goen's on one the server refused. */
.ui-input:user-valid, .ui-textarea:user-valid { border-color: var(--success); }
.ui-input[aria-invalid="true"], .ui-textarea[aria-invalid="true"],
.ui-input:user-invalid, .ui-textarea:user-invalid { border-color: var(--error); }
.ui-input:read-only:not([type="button"]), .ui-textarea:read-only {
  background: var(--panel); color: var(--fg-muted); cursor: default;
}
.ui-input:read-only:hover { border-color: var(--border); }

.ui-navitem:active { background: var(--panel); }

/* ---------- Commerce ---------- */

.ui-cart-line__meta { font-variant-numeric: tabular-nums; font-size: var(--fs-12); color: var(--fg-muted); }

/* ---------- The ground ----------

   Every value both surfaces are drawn from.

   The neutrals carry no chroma, because the product photographs sit on a
   chroma-free #f9f9f9 and any tint in the page would show as an edge around
   them. Colour is spent in two places only: the accent, which is also the
   chart's data colour, and the functional hues a status needs.
   ------------------------------------------------------------------------ */

:root {
  --n-0: #ffffff;
  --n-50: #fafafa;
  /* The ground baked into every product and category photograph. A container
     that is not this exact value draws a visible edge around the picture. */
  --well: #f9f9f9;
  --photo: var(--well);
  --photo-ground: var(--photo);
  --wash: #f5f5f4;
  --n-100: #f4f4f5;
  --n-200: #e7e7e9;
  --n-300: #d4d4d8;
  /* Icons and placeholders. Below 3:1 on white as text, so never body copy. */
  --n-400: #a1a1aa;
  /* The lightest a text colour may be: 5.4:1 on white, 5.1:1 on --n-50. */
  --n-500: #66666d;
  --n-700: #3f3f46;
  --n-900: #18181b;

  --ink: #1b1b1d;
  --ink-2: #3a3a3f;
  --muted: #5f5f66;
  --line: #e4e4e7;
  --line-2: #d1d1d6;
  /* The boundary of a control: 3:1 on white, --wash and --well. */
  --edge: #85858c;

  /* The one hue, 藍 (blue): what a shopper can press, where they are, and
     how far a period has run. 7.15:1 on white, so it is the link text as well
     as the ring and the fill; a price is never blue, or it reads as a link.
     --mark marks a stretch of time on the storefront; a chart's dates are drawn
     in --ink-2. The second ring of the goen mark is this blue too. 藍墨 (ink blue) is the same hue pressed:
     hover, text on a blue tint, and a label that is not an action. The tints
     are opaque so text on them is measured against one colour. */
  --accent: #2a56a0;
  --accent-deep: #26395a;
  --mark: #2a56a0;
  /* The part of a period still to come: the mark at 30%. Unlike the tints it
     stays translucent, so on a tone it is the tone's ground under the blue.
     assets/contrast_test.go lays it on every ground the period sits on: dark
     enough to show the track's extent, light enough for the fill to read on it. */
  --period-track: rgb(42 86 160 / 0.3);
  /* The track under the ink tone's fill, --on-ink-accent at 30%. */
  --period-track-on-ink: rgb(169 192 234 / 0.3);
  --accent-text: var(--accent);
  --accent-text-hover: var(--accent-deep);
  --accent-faint: #eef1f7;
  --accent-muted: #e2e8f2;
  --accent-strong: var(--accent-text);
  --on-accent: #ffffff;
  /* The blue on the ink tone, where --accent is 2.5:1. */
  --on-ink-accent: #a9c0ea;
  /* The focus ring. A dark ground sets it to white, because the accent is
     2.5:1 on #18181b. */
  --ring: var(--accent);

  /* The names the existing rules were written against, now on the ramp. */
  --bg: var(--n-0);
  --panel: var(--n-0);
  --fg: var(--ink);
  --fg-muted: var(--muted);
  --border: var(--line);
  --border-faint: var(--n-100);
  --border-strong: var(--line-2);
  /* Controls need 3:1 against their ground: 5.7:1 on white, 5.2:1 on n-100. */
  --control-boundary: var(--edge);
  --elevated: var(--n-100);
  --overlay: var(--n-100);

  /* Functional colours: a status says what it is by its words and its icon,
     the colour only agrees. Each ink clears 4.5:1 on its own tint. */
  --star: #b07c00;
  --error: #b91c1c;
  --error-bg: #fef2f2;
  --error-bg-hover: #f7dddd;
  --error-outline: #fca5a5;
  --success: #15803d;
  --success-bg: #f0fdf4;
  --warn: #92400e;
  --warn-bg: #fffbeb;
  --error-tint: #fee2e2;
  --success-tint: #dcfce7;
  --warn-tint: #fef3c7;
  /* The warning as a mark with no text on it: the triangle and the bar. 3:1 on
     the white and grey grounds a bar is drawn on. */
  --warn-mark: #b45309;
  --info: var(--accent-deep);
  --info-bg: var(--accent-faint);

  --font-display: var(--font-sans);

  /* One family for everything, headings included. Noto Sans TC follows
     Instrument Sans so Han is covered by a face that sits beside it rather
     than under a second personality. */
  --font-sans: 'Instrument Sans', 'Noto Sans TC', ui-sans-serif, system-ui, sans-serif;
  --font-editorial: 'Noto Serif TC', serif;

  /* Noto Sans TC draws 350; Instrument Sans starts at 400, so Latin and figures in a display title draw at 400. */
  --fw-display: 350;

  --t-12: 0.75rem;
  --t-13: 0.8125rem;
  --t-14: 0.875rem;
  --t-15: 0.9375rem;
  --t-16: 1rem;
  --t-18: 1.125rem;
  --t-20: 1.25rem;
  --t-24: 1.5rem;
  --t-28: clamp(1.5rem, 1.2rem + 0.9vw, 1.75rem);
  --t-32: clamp(1.625rem, 1.2rem + 1.2vw, 2rem);
  --t-44: clamp(2rem, 1.3rem + 2.2vw, 2.75rem);

  --s-1: 4px;
  --s-2: 8px;
  --s-3: 12px;
  --s-4: 16px;
  --s-5: 24px;
  --s-6: 32px;
  --s-7: 48px;
  --s-8: 64px;
  --s-9: 96px;
  --s-10: 128px;

  --fs-15: 0.9375rem;
  --fs-ed-24: 1.5rem;
  --fs-ed-30: 1.875rem;
  --fs-ed-38: 2.25rem;

  --r-sm: 0.25rem;
  --r-md: 0.375rem;
  --r-lg: 0.5rem;
  --r-xl: 0.5rem;
  --radius: var(--r-lg);

  /* A button carries the first; menus and popovers the second; drawers and
     dialogs the third. Nothing else casts a shadow. */
  --sh-xs: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --sh-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
  --sh-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
  --shadow-1: var(--sh-xs);
  --shadow-2: var(--sh-lg);

  /* The hairline a swatch needs so a near-white colour is still a disc on a
     white ground. */
  --swatch-ring: rgb(0 0 0 / 0.1);

  /* A select's arrow, in --n-500. */
  --chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2366666d' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}

/* ---------- Charts ----------

   One data colour, the accent's blue: "this period" on a chart and the
   storefront's period fill say the same thing. Its contrast against the page
   grounds is held by assets/contrast_test.go, so it stays a six-digit hex.
   Position, length and the row's own text carry the meaning, never the hue
   alone.
   ------------------------------------------------------------------------ */

:root {
  --chart-hue: #2a56a0;
  --chart-hue-track: #d4ddec;
  --chart-previous: #8e8e96;
  --chart-grid: var(--n-200);
  --chart-annotation: var(--fg);
  --chart-label: var(--fg-muted);
}

/* The back office's content column: it stops at 1280px inside a gutter of
   16 / 24 / 32px. app.css sets the storefront's own pair after this, and
   `scripts/check-layout.mjs` compares the header bar's, the home page's and
   the footer grid's content edges and fails when they disagree, so each reads
   the column from that one pair. */
:root {
  --page-gutter: 16px;
  --page-width: 1280px;
  /* The content column plus its gutters, for the pages that size themselves
     with border-box: their text then starts where the header's does. */
  --page-box: calc(var(--page-width) + 2 * var(--page-gutter));
  /* Where the content column starts on a full-bleed band. */
  --page-edge: max(var(--page-gutter), calc((100vw - var(--page-width)) / 2));
}

@media (min-width: 768px) {
  :root {
    --page-gutter: 24px;
  }
}

@media (min-width: 1024px) {
  :root {
    --page-gutter: 32px;
  }
}

/* ---------- Period ----------
   A stored start and end drawn to scale as one quiet track, a cell per day. A
   cell has no width of its own: the count of cells is the data. Elapsed cells
   are the fill and the cells to come the track; a mark breaks the track with a
   4px gap, and the span after it (the days goen adds to the statutory return
   window) is dashed. Today's cell is half filled, so a last day does not read
   as over. The track carries no words: the facts above it state the end and
   what is left, and aria-label holds the whole sentence, so the cells' labels
   are not drawn. Nothing animates and nothing is red. */
.ui-period {
  --period-fill: var(--mark);
  align-items: center;
  display: flex;
  margin-top: var(--s-5);
  max-width: 32rem;
}

.ui-period > i {
  border-bottom: 4px solid var(--period-track);
  flex: 1 1 0;
  font-style: normal;
  height: 0;
  min-width: 0;
}

.ui-period > i[data-cell] {
  border-bottom-color: var(--period-fill);
}

.ui-period > i:first-child,
.ui-period > i[data-mark] + i {
  border-bottom-left-radius: 2px;
  border-top-left-radius: 2px;
}

.ui-period > i:last-child,
.ui-period > i[data-mark] {
  border-bottom-right-radius: 2px;
  border-top-right-radius: 2px;
}

.ui-period > i[data-mark]:not(:last-child) {
  margin-right: 4px;
}

.ui-period b,
.ui-period small {
  display: none;
}

/* A dashed border would break each cell into its own uneven dashes, so the
   dashes and the half-filled day are gradients under a transparent border, and
   today among the dashes is the half-filled day cut by a mask. */
@media (forced-colors: none) {
  .ui-period > i[data-span="extra"] {
    background: linear-gradient(90deg, var(--period-track) 60%, transparent 0) 0 0 / 8px 100% round border-box;
    border-bottom-color: transparent;
  }

  .ui-period > i[data-span="extra"][data-cell] {
    background-image: linear-gradient(90deg, var(--period-fill) 60%, transparent 0);
  }

  .ui-period > i[data-span="extra"][data-cell="today"] {
    background: linear-gradient(90deg, var(--period-fill) 50%, var(--period-track) 0) border-box;
    mask: linear-gradient(90deg, #000 60%, transparent 0) 0 0 / 8px 100% round border-box;
  }

  .ui-period > i[data-cell="today"]:not([data-span]) {
    background: linear-gradient(90deg, var(--period-fill) 50%, var(--period-track) 0) border-box;
    border-bottom-color: transparent;
  }
}

/* Thickness carries elapsed from to come where the colours are the system's.
   A dashed border paints solid on a short cell and runs together on a long
   one, so the dashes and today's half are system-colour gradients here too,
   which forced-color-adjust: none keeps from being dropped. */
@media (forced-colors: active) {
  .ui-period > i {
    border-bottom: 2px solid GrayText;
  }

  .ui-period > i[data-cell] {
    border-bottom: 4px solid CanvasText;
  }

  .ui-period > i[data-span="extra"],
  .ui-period > i[data-cell="today"] {
    border-bottom-color: transparent;
    forced-color-adjust: none;
  }

  .ui-period > i[data-span="extra"] {
    background: linear-gradient(90deg, GrayText 60%, transparent 0) 0 0 / 8px 100% round border-box;
  }

  .ui-period > i[data-span="extra"][data-cell] {
    background-image: linear-gradient(90deg, CanvasText 60%, transparent 0);
  }

  .ui-period > i[data-span="extra"][data-cell="today"],
  .ui-period > i[data-cell="today"]:not([data-span]) {
    background:
      linear-gradient(CanvasText, CanvasText) 0 0 / 50% 100% no-repeat border-box,
      linear-gradient(GrayText, GrayText) 100% 50% / 50% 2px no-repeat border-box;
  }

  .ui-period > i[data-span="extra"][data-cell="today"] {
    mask: linear-gradient(90deg, #000 60%, transparent 0) 0 0 / 8px 100% round border-box;
  }
}

/* An SVG keeps the colours it was drawn in, so on a dark Canvas the near-black
   ring vanishes; below 1024px the mark is the only thing left of the home link. */
@media (forced-colors: active) {
  .goen-header__mark circle,
  .goen-footer__mark circle,
  .goen-adminbar__mark circle {
    stroke: CanvasText;
  }
}
