/* GateIron's chrome: the tokens, the two faces, the skip link, the top bar, the account menu,
   buttons and the footer. Every rule names a gi- class (or .btn), so a page with its own layout can
   load this file alone and keep its own look everywhere else. brand.css imports it and adds the
   page rules.

   A site reskins the chrome through custom properties, never by overriding its rules. The role
   tokens on :root below (--bg, --surface, --surface-2, --text, --text-muted, --accent,
   --accent-hover, --accent-on, --border-color, --border-strong, --field-border, --focus, --shadow,
   --ok/--warn/--urgent/--quiet and their -bg, --radius, --radius-pill) restyle everything that
   uses them, the chrome and brand.css alike. The palette above them (--paper-*, --ink-*, --clay-*,
   --pine-*, --ochre-*, --sand) is GateIron's and is not for overriding: change the role instead.
   test/contrast.test.js holds every pairing to WCAG's ratios in both schemes.

   These change one component only, set on :root or on the component; GateIron's values are on
   :root below:

     --bar-bg                  the bar's translucent ground
     --bar-max                 the bar's and footer's content cap (unset: the whole window)
     --gi-bar-size-min         the bar's height on a short window
     --gi-bar-size-std         the same, from --gi-scale-std-from to --gi-scale-std-to tall
     --gi-bar-size-max         the same, on a tall window
     --gi-footer-size-min      the footer's mark, which sets its spacing, on a short window
     --gi-footer-size-std      the same, between the two standard heights
     --gi-footer-size-max      the same, on a tall window
     --gi-scale-min-at         the window height at and below which both are at their minimum
     --gi-scale-std-from       the height from which both are at their standard
     --gi-scale-std-to         the height to which both stay at their standard
     --gi-scale-max-at         the height at and above which both are at their maximum
     --gi-avatar-bg            initials' circle
     --gi-avatar-accent-bg     the same, for an accented account
     --gi-avatar-text          initials
     --gi-footer-bg            the footer's ground
     --gi-footer-text          its text and links
     --gi-footer-strong        the company name, a hovered link
     --gi-footer-muted         the locality and the fine print
     --gi-footer-rule          the line above the fine print

   Between those heights the sizes are straight lines, so neither steps as the window changes.
   Text in them has rem floors and still grows with browser zoom. --bar-h is the bar's height, for
   content sized as 100svh minus the bar: it is read, not set, and holds while the bar's content
   fits its row (on a narrow screen, its two rows).

   The footer's gate is the light one, drawn for a dark ground. */

/* Faces are self-hosted. */
@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('fonts/fraunces-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'Hanken Grotesk';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('fonts/hanken-grotesk-latin.woff2') format('woff2');
}

:root {
  /* paper / surface tones (light → dark) */
  --paper-lightest: #f9f5eb;
  --paper-lighter: #f8f3e8;
  --paper-light: #f6f1e5;
  --paper: #f5efe3;
  --paper-dark: #efe6d4;
  --paper-darker: #e5d6bb;
  --paper-darkest: #ddcaa8;
  --card-bg: #fbf7ee;

  /* ink / text tones */
  --ink: #2a2520;
  --ink-light: #4a443b;
  --ink-lighter: #6b6255;
  --ink-lightest: #aea69a;

  /* clay accent */
  --clay-lightest: #eed8c8;
  --clay-lighter: #e0b59f;
  --clay-light: #cf8c6d;
  --clay: #bb5a31;
  --clay-dark: #9b4623;
  --clay-darker: #74371e;

  /* pine accent */
  --pine-lightest: #d5d5ca;
  --pine-lighter: #acb0a3;
  --pine-light: #7b8474;
  --pine: #3f4e3b;
  --pine-dark: #2c3829;

  /* A third signal between "fine" and "urgent", drawn from --sand. */
  --ochre-light: #e6c98a;
  --ochre: #a97c22;
  --ochre-dark: #7c5a16;

  --sand: #dcc9a6;

  /* roles - the only names page rules should use */
  --bg: var(--paper-light);
  --surface: var(--card-bg);
  --surface-2: var(--paper-dark);
  --text: var(--ink);
  --text-muted: var(--ink-lighter);
  /* --clay itself is under 4.5:1 as text on paper and under white text. */
  --accent: var(--clay-dark);
  --accent-hover: var(--clay-darker);
  --accent-on: #fffaf3;
  --border-color: rgba(42, 37, 32, 0.14);
  --border-strong: rgba(42, 37, 32, 0.28);
  --field-border: var(--text-muted);
  --shadow: 0 1px 2px rgba(42, 37, 32, 0.06), 0 12px 30px -12px rgba(42, 37, 32, 0.18);
  --focus: var(--clay-dark);

  --ok: var(--pine);
  --ok-bg: var(--pine-lightest);
  --warn: var(--ochre-dark);
  --warn-bg: #f3e7c8;
  --urgent: var(--clay-dark);
  --urgent-bg: var(--clay-lightest);
  --quiet: var(--ink-lighter);
  --quiet-bg: var(--paper-dark);

  --radius: 0.5rem;
  --radius-pill: 999px;
  --bar-bg: rgba(245, 239, 227, 0.82);

  /* brand.css's measures: the page, running text, a form. */
  --shell: 96rem;
  --gi-measure: 42rem;
  --gi-form-width: 48rem;

  --gi-bar-size-min: 2.5rem;
  --gi-bar-size-std: 3.75rem;
  --gi-bar-size-max: 4.5rem;
  --gi-footer-size-min: 1.5rem;
  --gi-footer-size-std: 2.25rem;
  --gi-footer-size-max: 2.75rem;
  --gi-scale-min-at: 400px;
  --gi-scale-std-from: 1024px;
  --gi-scale-std-to: 1200px;
  --gi-scale-max-at: 1600px;

  --gi-avatar-bg: var(--pine);
  --gi-avatar-accent-bg: var(--clay-dark);
  --gi-avatar-text: var(--paper-lightest);
  --gi-footer-bg: var(--ink);
  --gi-footer-text: rgba(243, 236, 221, 0.78);
  --gi-footer-strong: #f3ecdd;
  --gi-footer-muted: rgba(243, 236, 221, 0.62);
  --gi-footer-rule: rgba(243, 236, 221, 0.12);
}

/* Warm charcoals rather than neutral greys, so clay and pine keep their
   temperature. Accents move a rung lighter; the dark rungs go muddy here. */
/* Opt-in: page() adds .gi-dark-auto to <html> for darkMode: 'auto'. */
@media (prefers-color-scheme: dark) {
  :root.gi-dark-auto {
    --bg: #17140f;
    --surface: #211d17;
    --surface-2: #2b2620;
    --text: #ece5d8;
    --text-muted: #a89d8c;
    --accent: var(--clay-light);
    --accent-hover: var(--clay-lighter);
    --accent-on: #1a1611;
    --bar-bg: rgba(33, 29, 23, 0.82);
    --border-color: rgba(236, 229, 216, 0.14);
    --border-strong: rgba(236, 229, 216, 0.3);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 14px 34px -14px rgba(0, 0, 0, 0.75);
    --focus: var(--clay-lighter);

    --ok: #9db08f;
    --ok-bg: rgba(157, 176, 143, 0.16);
    --warn: var(--ochre-light);
    --warn-bg: rgba(230, 201, 138, 0.14);
    --urgent: var(--clay-light);
    --urgent-bg: rgba(207, 140, 109, 0.16);
    --quiet: var(--ink-lightest);
    --quiet-bg: rgba(236, 229, 216, 0.08);
  }
}

/* ---------------- the scale ----------------
   Worked out again wherever a token may be set, because a custom property takes the var()s of the
   element it is declared on. tan(atan2(a, b)) is a / b as a plain number. */
:root, .gi-compact, .gi-bar, .gi-footer {
  --gi-rise-1: clamp(0, tan(atan2(100svh - var(--gi-scale-min-at), var(--gi-scale-std-from) - var(--gi-scale-min-at))), 1);
  --gi-rise-2: clamp(0, tan(atan2(100svh - var(--gi-scale-std-to), var(--gi-scale-max-at) - var(--gi-scale-std-to))), 1);
  --gi-bar-size: calc(var(--gi-bar-size-min)
    + (var(--gi-bar-size-std) - var(--gi-bar-size-min)) * var(--gi-rise-1)
    + (var(--gi-bar-size-max) - var(--gi-bar-size-std)) * var(--gi-rise-2));
  --gi-footer-size: calc(var(--gi-footer-size-min)
    + (var(--gi-footer-size-std) - var(--gi-footer-size-min)) * var(--gi-rise-1)
    + (var(--gi-footer-size-max) - var(--gi-footer-size-std)) * var(--gi-rise-2));
  --gi-avatar-size: max(24px, var(--gi-bar-size) * 0.56);
  --bar-h: calc(var(--gi-bar-size) + var(--gi-nav-row, 0px) + 1px);
}

/* A game page pins the bar at its minimum (page({ density: 'compact' })); a classroom page, the
   footer. */
.gi-compact { --gi-bar-size-std: var(--gi-bar-size-min); --gi-bar-size-max: var(--gi-bar-size-min); }
.gi-compact .gi-bar .gi-wordmark small { display: none; }
.gi-footer-classroom { --gi-footer-size-std: var(--gi-footer-size-min); --gi-footer-size-max: var(--gi-footer-size-min); }

/* ---------------- chrome roots ----------------
   The chrome sets its own face, alignment and box model, so a page's own typeface or centring does
   not reach the bar or the footer; size and line height stay the page's. */
.gi-bar, .gi-footer, .gi-skip-link, .btn,
.gi-bar *, .gi-footer * { box-sizing: border-box; }
.gi-bar, .gi-footer { font-family: 'Hanken Grotesk', system-ui, sans-serif; text-align: start; }
/* A page's own img rule (a centring margin, a max-width) must not move the marks or the avatar. */
.gi-bar img, .gi-footer img { margin: 0; }
.gi-bar :focus-visible,
.gi-footer :focus-visible,
.btn:focus-visible,
.gi-skip-link:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 3px;
}

/* Read aloud, not shown. */
.gi-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.gi-skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--accent);
  color: var(--accent-on);
  padding: 0.6rem 1rem;
  z-index: 100;
}
.gi-skip-link:focus { left: 0; }

/* ---------------- top bar ---------------- */
.gi-bar {
  position: sticky;
  top: 0;
  z-index: 20;
  /* The paper tone at 82% over a blur, so content scrolling under it stays legible through it. */
  background: var(--bar-bg);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-color);
}

/* The whole window wide, so the account menu is in the corner where people look for it. */
.gi-bar-inner {
  max-width: var(--bar-max, none);
  margin: 0 auto;
  min-height: var(--gi-bar-size);
  padding: 0 28px;
  display: flex;
  align-items: center;
  gap: 0 1rem;
  flex-wrap: wrap;   /* the nav drops to its own line rather than disappearing */
}

.gi-brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  text-decoration: none;
  color: var(--text);
  white-space: nowrap;
  flex: 0 0 auto;
}
.gi-brand img { width: auto; display: block; }
.gi-bar .gi-brand { min-height: var(--gi-bar-size); }
.gi-bar .gi-brand img { height: calc(var(--gi-bar-size) * 0.6); }
.gi-wordmark {
  font-family: 'Fraunces', Georgia, serif;
  font-weight: 600;
  font-size: 1.3rem;
  letter-spacing: -0.01em;
  display: flex;
  flex-direction: column;
  line-height: 1.15;
}
.gi-bar .gi-wordmark { font-size: clamp(1rem, var(--gi-bar-size) * 0.3, 1.35rem); }
.gi-wordmark small {
  font-family: 'Hanken Grotesk', system-ui, sans-serif;
  font-weight: 500;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

/* Pushes the account chip right whatever sits between. */
.gi-bar-spacer { flex: 1 1 auto; }

.gi-nav {
  display: flex;
  align-items: center;
  gap: 0.15rem;
  flex-wrap: wrap;
}
.gi-nav a {
  display: inline-block;
  padding: 0.4rem 0.7rem;
  border-radius: var(--radius);
  color: var(--text);
  text-decoration: none;
  font-weight: 600;
  font-size: 0.93rem;
  line-height: 1.5;
}
.gi-nav a:hover { background: var(--surface-2); color: var(--text); }
/* The site's own buttons (actionsHtml). */
.gi-bar-actions { display: flex; align-items: center; gap: 0.5rem; }
.gi-nav a[aria-current="page"] {
  color: var(--accent);
  box-shadow: inset 0 -2px 0 var(--accent);
}

/* ---------------- account chip ---------------- */
.gi-account { position: relative; flex: 0 0 auto; }
.gi-account > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.2rem 0.2rem 0.2rem 0.55rem;
  border-radius: var(--radius-pill);
  border: 1.5px solid transparent;
}
.gi-account > summary::-webkit-details-marker { display: none; }
.gi-account > summary:hover,
.gi-account[open] > summary { border-color: var(--border-strong); }
.gi-account-who {
  font-size: 0.86rem;
  line-height: 1.15;
  text-align: right;
  color: var(--text-muted);
  max-width: 11rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gi-account-who b { display: block; color: var(--text); font-weight: 600; }
.gi-account-role { font-size: 0.8rem; }
.gi-account.gi-accent .gi-account-role { color: var(--accent); font-weight: 700; letter-spacing: 0.02em; }

.gi-avatar {
  width: var(--gi-avatar-size);
  height: var(--gi-avatar-size);
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--gi-avatar-bg);
  color: var(--gi-avatar-text);
  font-weight: 700;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  flex: 0 0 auto;
  border: 1.5px solid var(--border-color);
}
.gi-avatar.gi-accent { background: var(--gi-avatar-accent-bg); }
/* The image fills the circle; the size stays the bar's. */
.gi-avatar-img { padding: 0; overflow: hidden; }
.gi-avatar-img img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Holds the bar's height while a client script fills it. */
.gi-account-slot { display: flex; align-items: center; min-height: var(--gi-avatar-size); }

.gi-account-menu {
  position: absolute;
  right: 0;
  top: calc(100% + 0.5rem);
  min-width: 13rem;
  background: var(--surface);
  border: 1px solid var(--border-color);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 0.4rem;
  z-index: 30;
}
.gi-account-meta {
  padding: 0.4rem 0.6rem 0.55rem;
  border-bottom: 1px solid var(--border-color);
  margin-bottom: 0.35rem;
  font-size: 0.85rem;
  color: var(--text-muted);
  word-break: break-all;
}
.gi-account-menu a,
.gi-account-menu button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.45rem 0.6rem;
  border-radius: 0.35rem;
  border: none;
  background: none;
  font: inherit;
  font-size: 0.92rem;
  color: var(--text);
  text-decoration: none;
  cursor: pointer;
}
.gi-account-menu a:hover,
.gi-account-menu button:hover { background: var(--surface-2); }
.gi-account-menu form { margin: 0; }
.gi-account-menu [hidden] { display: none; }

/* ---------------- buttons ----------------
   .btn is the one button class every page uses, in the chrome and out of it. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  font: inherit;
  font-weight: 600;
  font-size: 0.95rem;
  padding: 0.55em 1.2em;
  border-radius: var(--radius-pill);
  border: 1.5px solid transparent;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.16s ease, border-color 0.16s ease, transform 0.16s ease;
}
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: none; }

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

/* Every button is solid (Jeff, 2026-10-12: consistency, and solid is preferred); .btn-secondary is
   kept as a name so existing markup needs no change. */
.btn-secondary { background: var(--accent); color: var(--accent-on); }
.btn-secondary:hover { background: var(--accent-hover); color: var(--accent-on); }

.btn-quiet { background: none; border-color: transparent; color: var(--text-muted); padding: 0.4em 0.6em; }
.btn-quiet:hover { color: var(--text); background: var(--surface-2); }

.btn-small { font-size: 0.85rem; padding: 0.3em 0.8em; }

/* A button in the bar keeps to the bar's height. */
.gi-bar .btn { padding-block: 0.15em; min-height: calc(var(--gi-bar-size) * 0.62); }

/* A button waiting on its form looks it. */
.btn:disabled,
.btn:disabled:hover { opacity: 0.45; cursor: not-allowed; transform: none; }
.btn-primary:disabled:hover,
.btn-secondary:disabled:hover { background: var(--accent); }
.btn-quiet:disabled:hover { background: transparent; }

/* ---------------- footer ----------------
   The company block sits at the bottom of every site, so the page ends saying
   GateIron even where the bar says the product. */
.gi-footer {
  flex-shrink: 0;
  background: var(--gi-footer-bg);
  color: var(--gi-footer-text);
  margin-top: calc(var(--gi-footer-size) * 1.45);
  padding: calc(var(--gi-footer-size) * 1.2) 0 calc(var(--gi-footer-size) * 0.9);
  font-size: 0.92rem;
}
.gi-footer-inner { max-width: var(--bar-max, none); margin: 0 auto; padding: 0 28px; }
.gi-footer-grid {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: calc(var(--gi-footer-size) * 0.7);
  flex-wrap: wrap;
}
.gi-footer .gi-brand { color: var(--gi-footer-strong); }
.gi-footer .gi-wordmark small { color: var(--gi-footer-muted); }
.gi-footer .gi-brand img { height: var(--gi-footer-size); }
.gi-footer .gi-wordmark { font-size: clamp(1rem, var(--gi-footer-size) * 0.47, 1.3rem); }
.gi-footer-links { display: flex; gap: 0.25rem calc(var(--gi-footer-size) * 0.6); flex-wrap: wrap; }
.gi-footer-links a { display: inline-block; padding-block: 0.15rem; }
.gi-footer a { color: var(--gi-footer-text); text-decoration: none; font-weight: 500; }
.gi-footer a:hover { color: var(--gi-footer-strong); text-decoration: underline; }
.gi-fine-print {
  margin-top: calc(var(--gi-footer-size) * 0.68);
  padding-top: calc(var(--gi-footer-size) * 0.5);
  border-top: 1px solid var(--gi-footer-rule);
  font-size: 0.82rem;
  color: var(--gi-footer-muted);
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}

/* ---------------- narrow screens ---------------- */
@media (max-width: 720px) {
  .gi-wordmark small { display: none; }
  /* Still the summary's name for a screen reader. */
  .gi-account-who {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
  }

  /* Brand and account share the first line; the nav, or a bar's own buttons, take the second, so
     its height is known. html.gi-bar-fits (menu.js, measured): they all fit on one line, so the
     nav or buttons sit between brand and account and --bar-h has no second row. */
  :root:not(.gi-bar-fits):has(.gi-bar .gi-nav) { --gi-nav-row: 2.4rem; }
  /* body.gi-bar-inline: the site's buttons stay on the first line; the site sees that they fit. */
  :root:not(.gi-bar-fits):has(.gi-bar .gi-bar-actions):not(:has(.gi-bar .gi-nav)):not(:has(body.gi-bar-inline)) { --gi-nav-row: 2.4rem; }
  :root:not(.gi-bar-fits) body:not(.gi-bar-inline) .gi-bar:not(:has(.gi-nav)) .gi-bar-actions { order: 3; flex-basis: 100%; justify-content: flex-end; gap: 0.4rem; }
  body:not(.gi-bar-inline) .gi-bar:not(:has(.gi-nav)) .gi-bar-actions { gap: 0.4rem; }
  body:not(.gi-bar-inline) .gi-bar:not(:has(.gi-nav)) .gi-bar-actions .btn { min-height: 2rem; padding: 0 0.65rem; font-size: 0.88rem; }
  .gi-bar-inner { gap: 0.4rem 0.5rem; padding: 0 0.75rem; }
  .gi-bar-spacer { display: none; }
  .gi-brand { flex: 1 1 auto; }
  .gi-account, .gi-account-slot { order: 2; }
  :root:not(.gi-bar-fits) .gi-nav { order: 3; flex-basis: 100%; gap: 0; }
  .gi-bar-fits .gi-nav, .gi-bar-fits .gi-bar-actions { order: 1; }
  .gi-bar-fits .gi-nav { gap: 0; }
  .gi-nav a { display: inline-flex; align-items: center; min-height: 2rem; padding: 0 0.55rem; font-size: 0.88rem; }
}

@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
  .btn:hover { transform: none; }
}
