/*
 * Gustly components: the one home for buttons, cards, toasts and the
 * livery pieces (band, wordmark, display type, stripes). Every state is here, in both themes,
 * from the tokens in styles.css only. A surface file adds placement, never a second look.
 *
 * Shapes are cut on ::before/::after (clip-path), never on the control, so the focus ring is
 * never clipped. Controls are upright boxes with one cut corner; only accents lean (--skew).
 * Every control is at least 44 px to the finger. Hover only where a pointer hovers. Motion only
 * in answer to an action, and none under reduced motion (the --dur tokens go to 0).
 *
 * The map keeps its old buttons: every .btn rule carries :where(:not(.map-pane *), .dock *), which
 * adds no specificity, and styles.css pins the old look inside the map (map freeze). The dock (the
 * spot's forecast under the map) wears the livery, so its buttons are these.
 *
 *   .btn                       base: 48 px, one cut corner; label in the UI face
 *   .btn.primary               the yellow go, label in the display face. One per view
 *   .btn.outline               secondary: a hard edge in the text colour
 *   .btn.quiet                 text only; a slanted bar slides under it on hover
 *   .btn.danger                destructive: rust edge and label, never filled
 *   .btn.icon                  a 48 px square for one icon (mic, send, close); add a variant
 *   .btn.google                Google sign-in: the primary with the G on its white tile
 *   [aria-pressed="true"]      a toggle that is on: yellow
 *   :disabled, [aria-disabled] grey fill, no edge
 *   [aria-busy="true"]         loading: the stripes run (still under reduced motion)
 *   .link-btn                  a button that reads as a link, in --accent-text
 *   controls.css: .field, .input, .select, .textarea, .hint, .label, .form-error
 *   .check (checkbox, radio), .switch (checkbox with role="switch"), .range
 *   .chip                      a parallelogram that selects: <button aria-pressed> or
 *                              <label class="chip"><input><span>
 *   .card, .card-h             one cut corner, raised
 *   .toast                     a navy band that slides in; data-tone="warn" | "danger" | "ok"
 *   .band, .wordmark, .display, .stripes
 */

/* ---------- Buttons ---------- */

.btn:where(:not(.map-pane *), .dock *) {
  --shape: polygon(0 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%);
  --fill: transparent;
  --edge: transparent;
  --edge-w: 2px;
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  min-height: 48px;
  min-width: var(--tap);
  padding: 0 22px 0 18px;
  border: 0;
  background: none;
  color: var(--text);
  font: 600 var(--t-body) / var(--lh-snug) var(--font-ui);
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-fast) var(--ease);
}

/* ::before is the edge, ::after the fill inset by the edge width; both carry the cut. */
.btn:where(:not(.map-pane *), .dock *)::before,
.btn:where(:not(.map-pane *), .dock *)::after {
  content: "";
  position: absolute;
  z-index: -1;
  clip-path: var(--shape);
  transition: background-color var(--dur-fast) var(--ease);
}

.btn:where(:not(.map-pane *), .dock *)::before {
  inset: 0;
  background: var(--edge);
}

.btn:where(:not(.map-pane *), .dock *)::after {
  inset: var(--edge-w);
  background: var(--fill);
}

.btn:where(:not(.map-pane *), .dock *) svg {
  flex: none;
}

/* Primary: the yellow go. The label is in the display face, the one thing to press. */
.btn.primary:where(:not(.map-pane *), .dock *) {
  --fill: var(--accent);
  --edge: var(--btn-edge);
  color: var(--accent-ink);
  font: 800 italic var(--t-d0) / var(--lh-snug) var(--font-display);
  letter-spacing: var(--track-display);
}

.btn.primary:where(:not(.map-pane *), .dock *):active:not(:disabled) {
  --fill: var(--volt-press);
}

/* Secondary: a hard edge in the text colour, the pane behind it. */
.btn.outline:where(:not(.map-pane *), .dock *) {
  --fill: var(--pane);
  --edge: var(--text);
}

.btn.outline:where(:not(.map-pane *), .dock *):active:not(:disabled) {
  --fill: var(--row);
}

/* Destructive: rust edge and label. Never filled, so it never competes with SEND IT. */
.btn.danger:where(:not(.map-pane *), .dock *) {
  --fill: var(--pane);
  --edge: var(--danger);
  color: var(--danger);
}

/* Quiet: text only; a slanted bar in the stripe colour slides under the label on hover. */
.btn.quiet:where(:not(.map-pane *), .dock *) {
  min-height: var(--tap);
  padding: 0 14px;
}

.btn.quiet:where(:not(.map-pane *), .dock *)::before {
  inset: auto 12px 8px;
  height: 3px;
  background: var(--stripe);
  clip-path: none;
  transform: skewX(var(--skew)) scaleX(0);
  transform-origin: left;
  transition: transform var(--dur) var(--ease);
}

.btn.quiet:where(:not(.map-pane *), .dock *)::after {
  inset: 0;
}

.btn.quiet:where(:not(.map-pane *), .dock *):active:not(:disabled) {
  --fill: var(--press);
}

.btn:where(:not(.map-pane *), .dock *):active:not(:disabled) {
  transform: translateY(1px);
}

/* A toggle that is on: yellow, whatever its variant. */
.btn:where(:not(.map-pane *), .dock *)[aria-pressed="true"] {
  --fill: var(--volt);
  --edge: var(--btn-edge);
  color: var(--on-volt);
}

/* ...except a destructive one: armed for its second press it stays rust, thicker, never the yellow of a go. */
.btn.danger:where(:not(.map-pane *), .dock *)[aria-pressed="true"] {
  --fill: var(--pane);
  --edge: var(--danger);
  --edge-w: 3px;
  color: var(--danger);
}

.btn.quiet:where(:not(.map-pane *), .dock *)[aria-pressed="true"]::before {
  inset: 0;
  height: auto;
  clip-path: var(--shape);
  transform: none;
  background: var(--btn-edge);
}

.btn.quiet:where(:not(.map-pane *), .dock *)[aria-pressed="true"]::after {
  inset: var(--edge-w);
}

.btn:where(:not(.map-pane *), .dock *):disabled,
.btn:where(:not(.map-pane *), .dock *)[aria-disabled="true"] {
  --fill: var(--disabled-bg);
  --edge: transparent;
  color: var(--disabled-text);
  cursor: not-allowed;
  transform: none;
}

.btn.quiet:where(:not(.map-pane *), .dock *):disabled,
.btn.quiet:where(:not(.map-pane *), .dock *)[aria-disabled="true"] {
  --fill: transparent;
}

/* Loading: stripes in the label's colour run across the fill; the label says what is happening. */
.btn:where(:not(.map-pane *), .dock *)[aria-busy="true"] {
  cursor: progress;
}

.btn:where(:not(.map-pane *), .dock *)[aria-busy="true"]::after {
  background:
    repeating-linear-gradient(104deg, transparent 0 10px, color-mix(in srgb, currentColor 22%, transparent) 10px 16px),
    var(--fill);
  background-size: 34px 100%;
  animation: stripes-run 0.6s linear infinite;
}

@keyframes stripes-run {
  to {
    background-position: 34px 0;
  }
}

/* Icon only: a square, the small cut. Give it an aria-label. */
.btn.icon:where(:not(.map-pane *), .dock *) {
  --cut: var(--cut-s);
  width: 48px;
  padding: 0;
}

/* Google sign-in: the primary, with Google's G on its own white tile (their branding rule). */
.btn.google:where(:not(.map-pane *), .dock *) {
  --fill: var(--accent);
  --edge: var(--btn-edge);
  gap: var(--s-3);
  padding-left: 8px;
  color: var(--accent-ink);
  font: 800 italic var(--t-d0) / var(--lh-snug) var(--font-display);
  letter-spacing: var(--track-display);
}

.btn.google:where(:not(.map-pane *), .dock *) .google-g {
  box-sizing: content-box;
  width: 20px;
  height: 20px;
  padding: 6px;
  background: var(--google-tile);
}

@media (hover: hover) {
  .btn.primary:where(:not(.map-pane *), .dock *):hover:not(:disabled, [aria-pressed="true"]),
  .btn.google:where(:not(.map-pane *), .dock *):hover {
    --fill: var(--volt-hover);
  }

  .btn.outline:where(:not(.map-pane *), .dock *):hover:not(:disabled, [aria-pressed="true"]),
  .btn.danger:where(:not(.map-pane *), .dock *):hover:not(:disabled) {
    --fill: var(--row);
  }

  .btn.outline:where(:not(.map-pane *), .dock *):hover:not(:disabled, [aria-pressed="true"]) {
    --edge: var(--focus);
  }

  .btn.quiet:where(:not(.map-pane *), .dock *):hover:not(:disabled)::before {
    transform: skewX(var(--skew)) scaleX(1);
  }

  .btn.icon.quiet:where(:not(.map-pane *), .dock *):hover:not(:disabled, [aria-pressed="true"]) {
    --fill: var(--press);
  }

  .btn.icon.quiet:where(:not(.map-pane *), .dock *):hover::before {
    transform: none;
  }

  .link-btn:hover {
    text-decoration-color: var(--focus);
  }
}

/* An icon button has no label to underline: its quiet bar stays away. */
.btn.icon.quiet:where(:not(.map-pane *), .dock *)::before {
  content: none;
}

/* A button that reads as a link: --accent-text, a thick underline, a full tap height. */
.link-btn {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 0 4px;
  border: 0;
  background: none;
  color: var(--accent-text);
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  cursor: pointer;
}

/* ---------- Card: raised, one cut corner ---------- */

.card {
  position: relative;
  isolation: isolate;
  display: grid;
  gap: var(--s-3);
  padding: var(--s-5);
}

.card::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--raise);
  box-shadow: var(--elev-1);
  clip-path: polygon(0 0, calc(100% - var(--cut-l)) 0, 100% var(--cut-l), 100% 100%, 0 100%);
}

.card-h {
  display: flex;
  align-items: center;
  gap: var(--s-2);
  margin: 0;
  font: 800 italic var(--t-d1) / 1 var(--font-display);
  letter-spacing: var(--track-display);
}

.card p {
  margin: 0;
}

/* ---------- Toast: a navy band with a wedge, sliding in once ---------- */

.toast {
  --wedge: var(--volt);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  max-width: 420px;
  min-height: 52px;
  padding: 0 var(--s-2) 0 0;
  background: var(--band);
  color: var(--band-text);
  font-weight: 600;
  box-shadow: var(--elev-2);
  animation: toast-in var(--dur-slow) var(--ease);
}

.toast::before {
  content: "";
  align-self: stretch;
  flex: none;
  width: 14px;
  background: var(--wedge);
  clip-path: polygon(0 0, 100% 0, 50% 100%, 0 100%);
}

.toast[data-tone="ok"] {
  --wedge: var(--ok);
}

.toast[data-tone="warn"] {
  --wedge: var(--warn);
}

.toast[data-tone="danger"] {
  --wedge: var(--danger);
}

.toast > :last-child:not(:first-child) {
  margin-left: auto;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
}

/* ---------- Livery ---------- */

/* The band's tokens are in styles.css, where the dark values make --pane the band's navy and --text
 * the band's text. This is its paint: pane with text, and a yellow 3 px rule. In the dark that is
 * navy and near-white, as before; in the sun every band (the top bar, the landing hero, the legal
 * bands) turns pane white with ink text and keeps its rule, so the verdict plate stays the loudest
 * thing on screen. */
.band {
  background: var(--pane);
  color: var(--text);
  box-shadow: inset 0 -3px 0 var(--volt);
}

/* The sunlit band takes the page's own light tokens back (the inherit lets the :root light block
 * flow in). The toast and the dock keep the navy tokens: they are not bands, and the feedback bars,
 * which float over any surface, stay navy. */
:where(:root[data-theme="light"]) .band:where(:not(.fb-snip-bar, .fb-replay)) {
  --text: inherit;
  --muted: inherit;
  --faint: inherit;
  --pane: inherit;
  --raise: inherit;
  --row: inherit;
  --line: inherit;
  --line-strong: inherit;
  --accent-text: inherit;
  --ok: inherit;
  --warn: inherit;
  --danger: inherit;
  --btn-edge: inherit;
  --focus: inherit;
  --focus-halo: inherit;
  --press: inherit;
  --stripe: inherit;
  color-scheme: light;
}

:where(:root[data-theme="light"]) .band:where(:not(.fb-snip-bar, .fb-replay)) :focus-visible {
  box-shadow: 0 0 0 calc(var(--focus-w) + 5px) var(--focus-halo);
}

/* "Gustly" in the display face, beside the untouched logo image. */
.wordmark {
  margin: 0;
  font: 800 italic var(--t-d2) / 1 var(--font-display);
  letter-spacing: var(--track-display);
}

/* Heavy italic condensed: calls, headings, numbers that shout. One weight, one style. */
.display {
  font-family: var(--font-display);
  font-weight: 800;
  font-style: italic;
  line-height: var(--lh-tight);
  letter-spacing: var(--track-display);
}

/* Three thin parallel slants: the brand's one ornament. */
.stripes {
  display: inline-block;
  width: calc(3 * (var(--stripe-w) + var(--stripe-gap)) + 6px);
  height: 1em;
  background: repeating-linear-gradient(
    104deg,
    transparent 0 var(--stripe-gap),
    var(--stripe) var(--stripe-gap) calc(var(--stripe-gap) + var(--stripe-w))
  );
  clip-path: polygon(6px 0, 100% 0, calc(100% - 6px) 100%, 0 100%);
}

@media (prefers-reduced-motion: reduce) {
  .btn:where(:not(.map-pane *), .dock *)[aria-busy="true"]::after,
  .toast {
    animation: none;
  }
}

/* The menu (public/js/ui/menu.js): one floating list for ⋯, the map's long-press and the app menu.
   Tokens only; text on --raise is 4.5:1 or better in both themes. A phone sheet is the same list
   pinned above the dock (.is-sheet). */
.menu {
  position: fixed;
  z-index: 40;
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
  min-width: 200px;
  max-width: calc(100vw - 16px);
  max-height: calc(100vh - 16px);
  padding: var(--s-2);
  overflow-y: auto;
  border: 1px solid var(--line-strong);
  background: var(--raise);
  color: var(--text);
  box-shadow: var(--elev-2);
}

.menu[hidden] {
  display: none;
}

.menu-head {
  margin: 0;
  padding: var(--s-1) var(--s-2);
  color: var(--faint);
  font-size: var(--t-xs);
  font-variant-numeric: tabular-nums;
}

.menu-item {
  display: flex;
  gap: var(--s-3);
  align-items: center; /* a row's icon sits beside its label, the hint at the far end */
  justify-content: flex-start;
  width: 100%;
  min-height: var(--tap);
  padding: var(--s-2) var(--s-3);
  border: 1px solid var(--line-strong);
  background: var(--raise);
  color: var(--text);
  font: inherit;
  font-size: var(--t-meta);
  text-align: left;
  cursor: pointer;
}

.menu-item:hover {
  border-color: var(--text);
}

.menu-item:focus-visible {
  outline: var(--focus-w) solid var(--focus);
  outline-offset: 0;
  box-shadow: 0 0 0 calc(var(--focus-w) + 1px) var(--focus-halo);
}

.menu-item.is-danger {
  color: var(--danger);
}

.menu-item[data-armed="true"] {
  border-color: var(--danger);
}

/* a checkable row: the check sits in the left gutter, the label keeps its place */
.menu-item[role="menuitemcheckbox"] {
  position: relative;
  padding-left: calc(var(--s-3) + 20px);
}

.menu-item[role="menuitemcheckbox"]::before {
  position: absolute;
  left: var(--s-3);
  content: "";
}

.menu-item[aria-checked="true"]::before {
  content: "\2713";
}

.menu-item small {
  margin-left: auto;
  color: var(--faint);
  font-size: var(--t-xs);
}

/* a phone: the same list as a sheet above the dock, full width, scrolling inside its max-height;
   over the open sidebar too (both sit at 40), so the app menu is never hidden under Sessions */
.menu.is-sheet {
  z-index: 41;
  top: auto;
  right: 0;
  left: 0;
  min-width: 0;
  max-width: none;
  border-width: 1px 0 0;
  padding-bottom: calc(var(--s-2) + env(safe-area-inset-bottom));
  box-shadow: none;
}

/* Forced colours drops the paints that make a button; keep an edge. */
@media (forced-colors: active) {
  .btn:where(:not(.map-pane *), .dock *) {
    outline: 1px solid ButtonText;
    outline-offset: -2px;
  }
}
