/* Bye Now — "Out of Office": a mid-century screen-printed leisure poster.
   Tokens and rules live in DESIGN.md; this file is their implementation.
   Fonts are self-hosted (see app/static/fonts) because the CSP is self-only. */

/* ---------------------------------------------------------------- fonts -- */
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 200 700;
  font-display: swap;
  src: url(/static/fonts/jost-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 200 700;
  font-display: swap;
  src: url(/static/fonts/jost-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Alfa Slab One';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/static/fonts/alfa-slab-one-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Alfa Slab One';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/static/fonts/alfa-slab-one-latin-ext.woff2) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* --------------------------------------------------------------- tokens -- */
:root {
  --cream: #f5e9d0;
  --cream-raised: #fdf6e7;
  --teal: #0f6b68;
  --teal-deep: #0a4f4d;
  --sun: #f2a02c;
  --burnt: #e0622a;
  --brown: #7a4a2b;
  --ink: #23301f;
  --muted: #5c6b56;
  --danger: #a33116;

  /* Derived from the palette above; documented in DESIGN.md. */
  --field: #ffffff;                     /* an input is a fresh sheet on the stock */
  --cream-hover: #fffdf3;
  --placeholder: #8a9184;
  --on-teal: #9dc4bf;                   /* secondary text on the inverted panel */
  --on-band: #e9e0c9;                   /* standfirst on the poster's teal band */
  --danger-bg: #fbe9e2;
  --hairline: rgba(15, 107, 104, .2);
  --hairline-strong: rgba(15, 107, 104, .35);
  --grain: rgba(122, 74, 43, .35);   /* the page's dot screen */
  --scrim: rgba(10, 79, 77, .84);    /* the dimmed page under the alert */

  --slab: 'Alfa Slab One', Rockwell, Georgia, serif;
  --sans: Jost, system-ui, -apple-system, 'Segoe UI', sans-serif;

  --rule: 2px solid var(--teal-deep);
  --rule-thick: 3px solid var(--teal-deep);
  --drop: 4px 4px 0 var(--teal-deep);

  --sp-xs: 6px;
  --sp-sm: 10px;
  --sp-md: 16px;
  --sp-lg: 26px;
  --sp-xl: 46px;
}

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

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
}

/* Print grain. Fixed and non-interactive so it never costs contrast. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: .3;
  background-image: radial-gradient(var(--grain) .6px, transparent .7px);
  background-size: 3px 3px;
}

body > * { position: relative; z-index: 1; }

h1, h2, h3 { font-family: var(--slab); font-weight: 400; letter-spacing: -.005em; }
p { margin: 0 0 1rem; }
a { color: var(--teal-deep); }
img { max-width: 100%; height: auto; }

:focus-visible { outline: 3px solid var(--sun); outline-offset: 2px; }

.tnum { font-variant-numeric: tabular-nums; }

/* Visible only to screen readers, and to keyboard users once focused. */
.skip {
  position: absolute; left: -9999px; top: 0; background: var(--teal-deep);
  color: var(--cream-raised); padding: 10px 16px; z-index: 5;
}
.skip:focus { left: 0; }

/* -------------------------------------------------------------- chrome --- */
body > header, main, body > footer {
  max-width: 1120px;
  margin: 0 auto;
  padding: 0 22px;
}

body > header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-md);
  flex-wrap: wrap;
  padding-top: var(--sp-md);
  padding-bottom: var(--sp-sm);
  border-bottom: var(--rule);
  margin-bottom: var(--sp-lg);
  /* Above `main`, which shares the same z-index-1 stacking context set on
     `body > *` and follows it in DOM order: without this the Settings
     dropdown paints under the page content it overlaps (#145). */
  position: relative;
  z-index: 2;
}

body > header h1 {
  margin: 0;
  font-size: 1.45rem;
  letter-spacing: .01em;
}
/* Says what the product is, for someone who has never seen it. Set in the sans
   so it reads as a description rather than a second half of the wordmark. */
body > header h1 .tagline {
  font-family: var(--sans);
  font-weight: 400;
  font-size: .95rem;
  letter-spacing: normal;
  color: var(--muted);
  margin-left: var(--sp-sm);
  white-space: nowrap;
}
body > header h1 a { color: var(--teal-deep); text-decoration: none; }
body > header h1 a::before {
  content: "";
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--sun);
  border: var(--rule);
  display: inline-block;
  vertical-align: -4px;
  margin-right: var(--sp-sm);
}

body > header nav ul {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px;
  margin: 0;
  padding: 0;
}
body > header nav a {
  text-decoration: none;
  color: var(--teal-deep);
  font-weight: 500;
  font-size: .95rem;
  text-transform: uppercase;
  letter-spacing: .09em;
  padding-bottom: 3px;
  border-bottom: 2px solid transparent;
}
body > header nav a:hover { border-bottom-color: var(--burnt); }
body > header nav a[aria-current] { color: var(--burnt); border-bottom-color: var(--burnt); }

/* Every top-level nav label is the same box (#148). An inline <a> is laid out
   against the <li>'s own 17px strut, and its padding and rule hang outside the
   line box; a <summary> is block-level (`display: list-item`) with a line box of
   its own and padding and rule that count. Two different boxes centred by the
   flex row put the Settings label 4px above its neighbours and its rule 2.4px
   short of theirs. Declaring one block box for all three — link, summary and
   the sign-out button — is what actually aligns them; a nudged margin would
   only hide it at one font size. */
body > header nav > ul > li > a,
body > header nav > ul > li > form > button[type="submit"],
body > header nav details.settings-menu > summary {
  display: block;
  line-height: 1.55;
}

/* Settings menu: eBay connection / Snipe timing / Billing, and Admin for
   admins only (#145). A native <details>/<summary> disclosure — no script
   needed to open or close it; settings-menu.js only adds the closes the
   platform has no opinion about (Escape, the mouse leaving, a press outside). */
body > header nav details.settings-menu { position: relative; }
body > header nav details.settings-menu summary {
  list-style: none;
  cursor: pointer;
  color: var(--teal-deep);
  font-weight: 500;
  font-size: .95rem;
  text-transform: uppercase;
  letter-spacing: .09em;
  padding-bottom: 3px;
  border-bottom: 2px solid transparent;
}
/* `display: block` above already drops the marker box; these two keep Safari
   and anything still drawing the old triangle from adding a second one. */
body > header nav details.settings-menu summary::-webkit-details-marker { display: none; }
/* The indicator, drawn rather than typed (#148). The \25be glyph it replaces
   rendered about 8px of hairline at .8em, which is what "make it bigger and
   more visible" was about. This is two 3px rules meeting at a corner — the same
   weight as the borders the rest of the poster is ruled with — turned 45deg so
   it points down, and 180deg further when the menu is open. It inherits
   `currentColor`, so it turns burnt with the label on hover and while open.
   The transform is layout-free: the 9px box sits below the cap height and
   never grows the line, which is what keeps the label aligned. Reduced motion
   is handled globally at the foot of this file. */
body > header nav details.settings-menu summary::after {
  content: "";
  display: inline-block;
  width: 9px;
  height: 9px;
  margin-left: 10px;
  border-right: 3px solid currentColor;
  border-bottom: 3px solid currentColor;
  transform: translateY(-4px) rotate(45deg);
  transition: transform .15s ease-out;
}
body > header nav details.settings-menu[open] summary::after {
  transform: translateY(-1px) rotate(225deg);
}
body > header nav details.settings-menu summary:hover,
body > header nav details.settings-menu[open] summary {
  color: var(--burnt);
  border-bottom-color: var(--burnt);
}
body > header nav details.settings-menu > ul {
  display: block;
  position: absolute;
  top: 100%;
  left: 0;
  margin: var(--sp-xs) 0 0;
  background: var(--cream-raised);
  border: var(--rule);
  box-shadow: var(--drop);
  padding: var(--sp-xs) 0;
  z-index: 3;
}
body > header nav details.settings-menu a {
  display: block;
  border-bottom: 0;
  padding: 9px 16px;
  white-space: nowrap;
}
body > header nav details.settings-menu a:hover,
body > header nav details.settings-menu a[aria-current] {
  background: var(--cream-hover);
  color: var(--burnt);
}

/* The sign-out form sits in the nav list and must not look like a form — and
   the `[type="submit"]` qualifier is what makes that true rather than merely
   intended. `.btn, button[type="submit"]` below carries a class-level
   selector, so it outranked a five-element chain and drew the header's Sign
   out as a burnt primary button a head taller than the links beside it. */
body > header nav form { margin: 0; }
body > header nav form button[type="submit"] {
  background: none;
  border: 0;
  border-bottom: 2px solid transparent;
  padding: 0 0 3px;
  cursor: pointer;
  color: var(--muted);
  font-family: var(--sans);
  font-weight: 500;
  font-size: .95rem;
  text-transform: uppercase;
  letter-spacing: .09em;
  box-shadow: none;
}
body > header nav form button[type="submit"]:hover {
  background: none;
  color: var(--burnt);
  border-bottom-color: var(--burnt);
}

body > footer {
  margin-top: var(--sp-xl);
  padding-top: var(--sp-md);
  padding-bottom: 34px;
  border-top: var(--rule);
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: .82rem;
}
body > footer p { margin: 0; }

main > section + section { margin-top: var(--sp-xl); }

/* --------------------------------------------------------------- poster -- */
.poster {
  position: relative;
  border: var(--rule-thick);
  overflow: hidden;
  background: var(--sun);
  margin: 0 0 var(--sp-lg);
}
/* Capped so the artwork plus its band still leaves the call to action in view. */
.poster img {
  display: block;
  width: 100%;
  max-height: 58vh;
  object-fit: cover;
  object-position: center 44%;
}
/* A solid printed band under the artwork rather than a fade over it: the
   illustration is busy, and a scrim cannot promise contrast everywhere. */
.poster .plate {
  padding: var(--sp-lg) 30px 24px;
  color: var(--cream-raised);
  background: var(--teal-deep);
  border-top: var(--rule-thick);
}
.poster h2 {
  font-size: clamp(1.7rem, 5.4vw, 3.4rem);
  line-height: 1.02;
  margin: 0 0 var(--sp-sm);
  max-width: 18ch;
  text-wrap: balance;
}
.poster h2 em { font-style: normal; color: var(--sun); }
/* #149: the standfirst runs at 1.45rem, not body size. The ask was "33% bigger";
   1.45rem (23.2px, +36.5%) is the ramp step that lands on. Exactly +33% would be
   22.61px — a tenth size in a nine-step ramp for a 0.59px difference nobody can
   see. The 56ch measure is deliberately unchanged: the larger type alone takes
   the longest line from 56% to 77% of the poster band. */
.poster .plate p { margin: 0; font-size: 1.45rem; max-width: 56ch; color: var(--on-band); }
.poster .stamp {
  position: absolute;
  top: 20px; right: 20px;
  /* Cream on burnt only reached 3.2:1 at this size; on teal it clears 4.5:1. */
  background: var(--teal-deep);
  color: var(--cream-raised);
  border: 2px solid var(--sun);
  padding: 8px 14px;
  transform: rotate(6deg);
  font-family: var(--slab);
  font-size: .95rem;
  letter-spacing: .04em;
  text-transform: uppercase;
}

/* -------------------------------------------------------------- buttons -- */
.btn,
button[type="submit"],
.empty a {
  display: inline-block;
  font-family: var(--slab);
  font-size: 1.1rem;
  letter-spacing: .02em;
  text-decoration: none;
  padding: 13px 26px;
  border: var(--rule);
  background: var(--burnt);
  color: var(--cream-raised);
  box-shadow: var(--drop);
  cursor: pointer;
}
.btn:hover,
button[type="submit"]:hover,
.empty a:hover { background: var(--sun); color: var(--teal-deep); }
.btn:focus-visible,
button[type="submit"]:focus-visible { outline: 3px solid var(--teal); outline-offset: 4px; }
.btn.secondary { background: var(--sun); color: var(--teal-deep); }

.actions {
  display: flex;
  gap: var(--sp-md);
  flex-wrap: wrap;
  align-items: center;
}
.actions p { margin: 0; color: var(--muted); max-width: 44ch; font-size: .95rem; }

/* ------------------------------------------------------------- promises -- */
.promises {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border: var(--rule);
  background: var(--cream-raised);
  margin-top: var(--sp-lg);
}
.promises div { padding: var(--sp-md) 18px; border-right: var(--rule); }
.promises div:last-child { border-right: 0; }
.promises b {
  display: block;
  font-family: var(--slab);
  font-weight: 400;
  font-size: 1.1rem;
  color: var(--teal-deep);
  margin-bottom: 4px;
}
.promises span { font-size: .95rem; color: var(--muted); }

/* ------------------------------------------------------- section headers -- */
.sechead {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-md);
  flex-wrap: wrap;
  margin: var(--sp-xl) 0 14px;
}
main > section:first-child .sechead { margin-top: 0; }
.sechead h2 { margin: 0; font-size: clamp(1.5rem, 3.4vw, 2.1rem); color: var(--teal-deep); }
.sechead .note {
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  color: var(--muted);
}

/* -------------------------------------------------------------- add bar -- */
.addbar {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: center;
  background: var(--teal-deep);
  border: var(--rule);
  padding: 14px 16px;
  margin-bottom: var(--sp-lg);
}
.addbar label {
  color: var(--cream-raised);
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: .82rem;
}
.addbar input {
  flex: 1;
  min-width: 220px;
  border: 2px solid var(--cream-raised);
  background: var(--cream-raised);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 1rem;
  padding: 11px 13px;
}
.addbar button[type="submit"] {
  background: var(--sun);
  color: var(--teal-deep);
  box-shadow: none;
  border-color: var(--cream-raised);
  font-size: 1rem;
  padding: 11px 22px;
}
.addbar button[type="submit"]:hover { background: var(--burnt); color: var(--cream-raised); }

/* --------------------------------------------------------- ticket rows --- */
.ticket {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 250px;
  background: var(--cream-raised);
  border: var(--rule);
  border-top: 0;
  text-decoration: none;
  color: inherit;
}
.ticket:first-of-type { border-top: var(--rule); }
.ticket:hover { background: var(--cream-hover); }
.ticket:focus-visible { outline: 3px solid var(--burnt); outline-offset: -3px; }

.ticket .body {
  padding: 18px 20px;
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: var(--sp-md);
  align-items: center;
}
.ticket .thumb {
  width: 88px; height: 88px;
  background: var(--sun);
  border: var(--rule);
  object-fit: cover;
  display: grid;
  place-items: center;
  text-align: center;
  font-size: .72rem;
  line-height: 1.25;
  color: var(--teal-deep);
  padding: 6px;
}
.ticket h3 {
  margin: 0 0 var(--sp-xs);
  font-family: var(--sans);
  font-weight: 600;
  font-size: 1.1rem;
  line-height: 1.25;
}
.ticket .meta { margin: 0; font-size: .95rem; color: var(--muted); }

.ticket .stub {
  position: relative;
  padding: 18px 22px;
  text-align: right;
  background: var(--cream);
  border-left: 2px dashed var(--teal-deep);
}
/* Punched notches where the stub tears off. */
.ticket .stub::before,
.ticket .stub::after {
  content: "";
  position: absolute;
  left: -11px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--cream);
  border: var(--rule);
}
.ticket .stub::before { top: -11px; clip-path: inset(50% 0 0 0); }
.ticket .stub::after { bottom: -11px; clip-path: inset(0 0 50% 0); }

.stub .label,
.facts dt,
.field label,
.stat .label {
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--muted);
  display: block;
}
.stub .clock {
  display: block;
  font-family: var(--slab);
  font-size: 2.2rem;
  line-height: 1.05;
  color: var(--burnt);
  font-variant-numeric: tabular-nums;
}
.stub .clock.ended { color: var(--teal-deep); font-size: 1.6rem; }
.stub .exact { display: block; font-size: .95rem; color: var(--muted); margin-top: 2px; }
.stub .ceiling { margin-top: var(--sp-sm); padding-top: 9px; border-top: 1px solid var(--hairline-strong); }
.stub .ceiling b {
  font-family: var(--slab);
  font-weight: 400;
  font-size: 1.1rem;
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- tags --- */
.tag {
  display: inline-block;
  margin-top: 9px;
  padding: 4px 12px;
  border: var(--rule);
  font-size: .82rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .07em;
}
.tag.armed { background: var(--teal-deep); color: var(--cream-raised); }
.tag.idle { background: transparent; color: var(--muted); border-style: dashed; }
.tag.won { background: var(--sun); color: var(--teal-deep); }
.tag.lost { background: transparent; color: var(--danger); border-color: var(--danger); }

/* ------------------------------------------------ no-image placeholder --- */
/* #136: a listing with no photo gets a real image — the vendored
   static/img/no-image-placeholder.svg — in the very box a photo would have
   filled, on the sun ground real thumbnails already sit on. `contain`, not
   the `cover` a photo gets: the art is portrait and lettered, so cropping it
   would eat the words. Its sentence lives in the alt attribute
   (app/presentation.py, NO_IMAGE), which is why nothing here draws text. */
.noimg {
  background: var(--sun);
  object-fit: contain;
  padding: 8px;
}

/* -------------------------------------------------------------- detail --- */
.detail { border: var(--rule-thick); background: var(--cream-raised); }

/* The listing photo band, between the inverted head and the facts. A photo
   sits on a fresh sheet; the no-photo tile brings its own sun ground. */
/* flow-root so the placeholder's own margin stays inside the band instead of
   collapsing out through it and printing a gap under the head. */
.detail .media { display: flow-root; border-bottom: var(--rule); background: var(--field); }
.detail .media img.media {
  display: block;
  width: 100%;
  max-height: 44vh;
  object-fit: contain;
}
/* The placeholder is centred at the band's height instead of stretched to
   its width: a photo earns the full sheet, a stand-in sits on the sun ground
   in a frame, exactly as it does in a ticket thumbnail. `width: auto` undoes
   the photo rule above, which this rule ties on specificity and follows. */
.detail .media img.noimg {
  display: block;
  width: auto;
  height: 34vh;
  margin: var(--sp-lg) auto;
  padding: var(--sp-md);
  /* Restated because .detail .media paints the band's fresh sheet and outruns
     .noimg on specificity. */
  background: var(--sun);
  border: var(--rule);
}
.detail .head {
  background: var(--teal-deep);
  color: var(--cream-raised);
  padding: 22px var(--sp-lg);
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 22px;
  align-items: end;
}
.detail .head .ref {
  display: block;
  font-size: .82rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--on-teal);
  margin-bottom: 8px;
}
.detail .head h2 {
  margin: 0;
  font-family: var(--sans);
  font-weight: 600;
  font-size: clamp(1.25rem, 3vw, 1.8rem);
  line-height: 1.15;
  max-width: 26ch;
}
.detail .head .clock {
  display: block;
  text-align: right;
  font-family: var(--slab);
  font-size: clamp(2rem, 6.5vw, 3.4rem);
  line-height: 1;
  color: var(--sun);
  font-variant-numeric: tabular-nums;
}
.detail .head .exact {
  display: block;
  text-align: right;
  font-size: .82rem;
  color: var(--on-teal);
  margin-top: 4px;
}

/* An item title inside an inverted head is content, so it stays in the sans. */
.detail .head h3 {
  margin: 0;
  font-family: var(--sans);
  font-weight: 600;
  font-size: clamp(1.25rem, 2.6vw, 1.45rem);
  line-height: 1.15;
  max-width: 26ch;
}

.detail .split { display: grid; grid-template-columns: 1fr 1fr; }

/* Two equal panels side by side (billing). */
.split-two { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-md); align-items: start; }

.facts { margin: 0; border-right: var(--rule); }
.facts > div {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  padding: 13px var(--sp-lg);
  border-bottom: 1px solid var(--hairline);
}
.facts dt { margin: 0; }
.facts dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; }
.facts dd.ok { color: var(--teal); }
.facts dd.warn { color: var(--danger); }

/* --------------------------------------------------------------- forms --- */
form.arming, form.stacked { padding: 22px var(--sp-lg) var(--sp-lg); margin: 0; }
/* Already inside a padded panel, so it brings no padding of its own. */
form.bare { padding: 0; }
form.arming h3, form.stacked h3 { margin: 0 0 var(--sp-md); color: var(--teal-deep); font-size: 1.25rem; }

.field { margin-bottom: 18px; }
.field label { margin-bottom: var(--sp-xs); }
.field .box { display: flex; align-items: center; border: var(--rule); background: var(--field); }
.field .box .prefix { padding: 0 2px 0 14px; color: var(--muted); font-family: var(--slab); font-size: 1.25rem; }
.field .box input::placeholder { color: var(--placeholder); }
.field .box input {
  border: 0;
  background: transparent;
  width: 100%;
  padding: 12px;
  font-family: var(--slab);
  font-size: 1.45rem;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.field .box:focus-within { outline: 3px solid var(--sun); outline-offset: 2px; }
.field .box input:focus { outline: none; }
/* Every vertical margin in this file is a *bottom* margin — `p { margin: 0 0 1rem }`
   sets the pattern — so the space above a block is whatever the block before it
   left behind. Help text is the one thing that habitually follows a control, and
   a control leaves nothing: a `button`/`.btn` is inline-block and sits in an
   anonymous block box with no margin at all, `form.arming`/`form.stacked` declare
   `margin: 0`, and `ul.plain` does too. That is why the hint under "Save my
   timing" measured a 0px gap and its first line ran under the button's 4px drop
   shadow, which is painted outside the layout box (#154). So the hint carries the
   gap itself. One scale step, and margins between adjacent block siblings
   collapse, so it is a no-op after a paragraph — 1rem there already. */
.hint { font-size: .95rem; color: var(--muted); margin-top: var(--sp-md); }
/* Except against its own input, where it stays tight on purpose: at this
   distance the sentence reads as part of the field rather than after it. */
.field .hint { margin: 7px 0 0; }
.hint.lead { margin-top: 0; }

/* Plain text inputs (auth, settings) stay in the sans at a normal size. */
.field.plain input {
  font-family: var(--sans);
  font-size: 1rem;
  padding: 12px 13px;
  border: var(--rule);
  background: var(--field);
  width: 100%;
  color: var(--ink);
}

form.arming button[type="submit"],
form.stacked button[type="submit"] { width: 100%; font-size: 1.25rem; }

/* --------------------------------------------------------------- panels -- */
.panel {
  border: var(--rule);
  background: var(--cream-raised);
  padding: 20px var(--sp-lg) var(--sp-lg);
}
.panel h2, .panel h3 { margin-top: 0; color: var(--teal-deep); }
/* The panel's padding is the whole gap at both ends. Without the `:first-child`
   half, the one `.hint` that opens a panel (the pricing panel's "Saved —" line)
   would add its new top margin to the 20px above it and sit lower than the
   heading it replaces. */
.panel > :first-child { margin-top: 0; }
.panel > :last-child { margin-bottom: 0; }

.narrow { max-width: 520px; margin-left: auto; margin-right: auto; }

.stat { margin-bottom: var(--sp-md); }
.stat .value { font-family: var(--slab); font-size: 2rem; line-height: 1.1; font-variant-numeric: tabular-nums; }
.stat .value.ok { color: var(--teal); }
/* A connection eBay has refused (#81). Never color alone — the word beside it
   is the status itself, and an alert states the consequence in full. */
.stat .value.bad { color: var(--danger); }

ul.plain { list-style: none; margin: 0; padding: 0; }
ul.plain li {
  padding: 11px 0;
  border-bottom: 1px solid var(--hairline);
  display: flex;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}
ul.plain li:last-child { border-bottom: 0; }
ul.plain code {
  font-size: .95rem;
  background: var(--cream);
  border: 1px solid var(--hairline-strong);
  padding: 2px 6px;
  word-break: break-all;
}

/* -------------------------------------------------------- empty & alerts -- */
.empty {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 24px;
  align-items: center;
  border: var(--rule);
  background: var(--cream-raised);
  padding: 20px;
}
.empty img { display: block; width: 100%; border: var(--rule); }
.empty h3 { margin: 0 0 8px; color: var(--teal-deep); font-size: 1.25rem; }
.empty p { margin: 0 0 14px; color: var(--muted); max-width: 52ch; }

/* #144: form-validation.js swaps `role` for `data-alert-promoted` once the
   dialog has announced a refusal, so the paragraph keeps its treatment without
   announcing itself a second time. */
[role="alert"],
[data-alert-promoted] {
  border: var(--rule);
  border-color: var(--danger);
  background: var(--danger-bg);
  color: var(--danger);
  padding: 11px 14px;
  margin: 0 0 var(--sp-md);
}

/* A refused form submission, said beside the field it refuses (#121): the
   oopsie spot, a slab heading, the refusal sentence, and — only when the fix
   lives on another page — the button that goes there. */
.refusal {
  display: grid;
  grid-template-columns: 84px minmax(0, 1fr);
  gap: var(--sp-md);
  align-items: center;
  border: var(--rule);
  border-color: var(--danger);
  background: var(--danger-bg);
  color: var(--ink);
  padding: var(--sp-md);
  margin: 0 0 var(--sp-md);
}
.refusal svg { display: block; width: 100%; height: auto; }
.refusal h3 { margin: 0 0 6px; font-size: 1.25rem; color: var(--danger); }
.refusal p { margin: 0; max-width: 52ch; }
.refusal .btn { margin-top: 14px; }

.notice {
  border: 2px dashed var(--brown);
  color: var(--brown);
  padding: 11px 14px;
  margin: 0 0 var(--sp-md);
  font-size: .95rem;
}

/* ---------------------------------------------------- prominent alert --- */
/* One callout for anything a form refuses (#144), anchored to the field that
   was refused. It replaces the browser's own validation bubble — six grey
   words under the box, gone in three seconds — and carries a server refusal at
   the same volume.

   The page dims, but the refused fields are punched out of the dim by
   form-validation.js and ringed in sun, so the alert says *where* as well as
   *what*. Poster furniture, not a red banner: cream stock, a hard rule and the
   house offset shadow, with the danger token confined to the stamp and the
   rule down the side of each refused line. DESIGN.md forbids invented urgency,
   so the loudness comes from the dimmed page and the lit field, never from
   pulsing or shouting copy. */
.alertcallout {
  position: fixed;
  inset: 0;
  z-index: 20;
}
.alertcallout[hidden] { display: none; }
/* Measured but not yet seen: a card has no size until it is in the layout, and
   it must never be seen anywhere but its final place. */
.alertcallout.placing { visibility: hidden; }

.alertcallout-scrim { position: absolute; inset: 0; background: var(--scrim); }
.alertcallout-rings { position: absolute; inset: 0; pointer-events: none; }

/* Drawn around each field the scrim was punched out for. Never color alone —
   the callout names every lit field in words. */
.alertcallout-lit {
  position: absolute;
  outline: 3px solid var(--sun);
  box-shadow: 0 0 0 7px rgba(242, 160, 44, .3);
}

.alertcallout-card {
  position: absolute;
  width: min(420px, calc(100vw - 24px));
  background: var(--cream-raised);
  border: var(--rule-thick);
  box-shadow: var(--drop);
  /* Visible on purpose: the pointer is a ::before that hangs outside the card,
     so the scrolling lives on the body within. */
  overflow: visible;
  animation: alert-in .16s ease-out;
}
.alertcallout-card:focus-visible { outline: 3px solid var(--sun); outline-offset: 4px; }
.alertcallout-body { padding: 20px 22px; overflow-y: auto; }

/* The pointer. Its offset along the edge is set by the script, which aims it
   at the field even after the card has been clamped away from it. */
.alertcallout-card::before {
  content: "";
  position: absolute;
  left: var(--point-x, 30px);
  margin-left: -10px;
  width: 20px;
  height: 20px;
  background: var(--cream-raised);
  transform: rotate(45deg);
}
.alertcallout-card[data-side="below"]::before {
  top: -12px;
  border-left: var(--rule-thick);
  border-top: var(--rule-thick);
}
.alertcallout-card[data-side="above"]::before {
  bottom: -12px;
  border-right: var(--rule-thick);
  border-bottom: var(--rule-thick);
}
/* Nowhere beside the field will take it — a short viewport, or no field to
   point at at all — so it centres and the pointer goes rather than aiming at
   nothing. The field it names stays punched out and lit behind it. */
.alertcallout-card[data-side="center"] { inset: 0; margin: auto; height: max-content; }
.alertcallout-card[data-side="center"]::before { display: none; }

/* The second ask, when the card at full size fits neither above nor below: the
   picture is decorative and repeats the words, so it is what goes first. */
.alertcallout-card.compact .alertcallout-art { display: none; }
.alertcallout-card.compact .alertcallout-head {
  grid-template-columns: minmax(0, 1fr);
  gap: 0;
  margin-bottom: var(--sp-sm);
}
.alertcallout-card.compact .alertcallout-body { padding: var(--sp-md) 18px; }

.alertcallout-head {
  display: grid;
  grid-template-columns: 72px minmax(0, 1fr);
  gap: 14px;
  align-items: center;
  margin-bottom: var(--sp-md);
}
/* Screen-printed spot art, generated for this alert (assets/alert-art). It
   repeats what the words say, so it is decorative and hidden from assistive
   tech in the template. */
.alertcallout-art { display: block; width: 72px; height: 72px; border: var(--rule); }

/* The poster's rotated stamp, doing the job the word "Error" would have done
   badly: it states what happened, not how to feel about it. */
.alertcallout-stamp {
  display: inline-block;
  margin: 0 0 8px;
  padding: 5px 12px;
  background: var(--danger);
  color: var(--cream-raised);
  border: var(--rule);
  transform: rotate(-3deg);
  font-family: var(--slab);
  font-size: .82rem;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.alertcallout-card h2 { margin: 0; color: var(--teal-deep); font-size: 1.25rem; }

.alertcallout-list { list-style: none; margin: 0 0 var(--sp-md); padding: 0; }
/* No coloured bar down the side: the rotated stamp, the dimmed page and the
   sun ring on the field itself already say this is a refusal, and a 4px slab
   of --danger was both a fourth shout and off the 2px/3px rule scale the rest
   of the poster is ruled with. The hairline below each row is how every other
   list in here separates its items. */
.alertcallout-list li {
  padding: 10px 0;
  border-bottom: 1px solid var(--hairline);
}
.alertcallout-list li:last-child { border-bottom: 0; }
/* The field's own label, in the same uppercase Jost every label uses, so the
   line names the box the user has to go back to. */
.alertcallout-list b {
  display: block;
  font-family: var(--sans);
  font-weight: 500;
  font-size: .82rem;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--muted);
}
.alertcallout-list span { display: block; }
.alertcallout-card .btn { width: 100%; text-align: center; font-size: 1rem; padding: 11px 20px; }

/* The page behind must not scroll while the callout is over it. */
html.alert-open, html.alert-open body { overflow: hidden; }

/* Marked by form-validation.js on every control the callout named, and cleared
   the moment one is typed into. Never color alone — the callout says which
   field and why in words, and this only points at it. */
.field .box:has([aria-invalid="true"]),
.field.plain input[aria-invalid="true"],
.addbar input[aria-invalid="true"] { border-color: var(--danger); }

@keyframes alert-in {
  from { transform: translateY(-6px); opacity: 0; }
  to { transform: none; opacity: 1; }
}

/* ------------------------------------------------------------ responsive -- */
@media (max-width: 900px) {
  .promises { grid-template-columns: 1fr 1fr; }
  .promises div:nth-child(2n) { border-right: 0; }
  .promises div:nth-child(-n+2) { border-bottom: var(--rule); }

  .ticket { grid-template-columns: 1fr; }
  .ticket .stub {
    text-align: left;
    border-left: 0;
    border-top: 2px dashed var(--teal-deep);
  }
  .ticket .stub::before { left: auto; right: -11px; top: -11px; clip-path: inset(0 0 0 50%); }
  .ticket .stub::after { display: none; }

  .detail .split { grid-template-columns: 1fr; }
  .facts { border-right: 0; border-bottom: var(--rule); }
  .detail .head { grid-template-columns: 1fr; }
  .detail .head .clock, .detail .head .exact { text-align: left; }

  .split-two { grid-template-columns: 1fr; }
  .empty { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  /* The art is the first thing to go when the card is this narrow: it repeats
     what the words already say, and the words have to stay readable. */
  .alertcallout-head { grid-template-columns: 1fr; gap: var(--sp-sm); }
  .alertcallout-art { display: none; }
  .alertcallout-body { padding: var(--sp-md) 18px; }
  .ticket .body { grid-template-columns: 64px minmax(0, 1fr); gap: 12px; }
  .ticket .thumb { width: 64px; height: 64px; font-size: .72rem; }
  .ticket .thumb.noimg { padding: 5px; }
  body > header { gap: var(--sp-sm); }
  body > header h1 .tagline { display: block; margin-left: 0; white-space: normal; }
  /* An absolutely-positioned panel can run past the edge of a phone-width
     viewport depending on where "Settings" lands in the wrapped row; drop it
     into normal flow instead, same as the rest of the nav already does. */
  body > header nav details.settings-menu > ul {
    position: static;
    border: 0;
    box-shadow: none;
    margin: var(--sp-xs) 0 0;
    padding: var(--sp-xs) 0 var(--sp-xs) var(--sp-md);
  }
  .poster .plate { padding: 18px 18px 16px; }
  .poster .stamp { top: 12px; right: 12px; font-size: .82rem; padding: 6px 10px; }
}

/* The pseudo-elements are listed because `*` does not match them, and the
   Settings chevron — the one thing in the nav that moves — is a ::after. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
}
