/* obvious components — the shared interaction layer. Import after primitives, a theme and base.
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

/* ob-badge. A count on a button or tab, like unread messages. It pops in
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-badge{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:20px;height:20px;padding:0 var(--ds-space-2xs);
  border-radius:var(--ds-radius-full);
  background:var(--ds-color-danger);
  color:var(--ds-color-accent-contrast);
  font-family:var(--ds-font-sans);
  font-size:var(--ds-text-micro);
  font-weight:var(--ds-weight-semibold);
  line-height:1;
  font-variant-numeric:tabular-nums;
  pointer-events:none;
  transition:scale var(--ds-duration-slow) var(--ds-ease-spring),opacity var(--ds-duration-fast) var(--ds-ease-out);
}
.ob-badge[data-visible="false"]{
  scale:0;opacity:0;
  transition:scale var(--ds-duration-fast) var(--ds-ease-in),opacity var(--ds-duration-fast) var(--ds-ease-in);
}

.ob-badge-host{position:relative}
.ob-badge-host > .ob-badge{
  position:absolute;top:-8px;right:-8px;
  box-shadow:0 0 0 2px var(--ds-color-bg);
}

@media (prefers-reduced-motion:reduce){
  .ob-badge{transition:none}
}

/* ob-button. Primary is the one thing you want people to press, secondary
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-button{
  --ob-button-height:var(--ds-control-height-md);
  --ob-button-pad:var(--ds-control-pad-x-md);
  --ob-button-radius:var(--ds-radius-md);
  --ob-button-bg:var(--ds-color-accent);
  --ob-button-bg-hover:var(--ds-color-accent-hover);
  --ob-button-fg:var(--ds-color-accent-contrast);
  --ob-button-border:transparent;

  display:inline-flex;align-items:center;justify-content:center;gap:var(--ds-space-xs);
  min-height:max(var(--ob-button-height),var(--ds-control-tap-min));
  padding:0 var(--ob-button-pad);
  border:var(--ds-border-hairline) solid var(--ob-button-border);
  border-radius:var(--ob-button-radius);
  background:var(--ob-button-bg);
  color:var(--ob-button-fg);
  font-family:var(--ds-font-sans);
  font-size:var(--ds-type-ui-size);
  font-weight:var(--ds-type-ui-weight);
  line-height:var(--ds-type-ui-leading);
  white-space:nowrap;
  text-decoration:none;
  cursor:pointer;
  user-select:none;
  -webkit-tap-highlight-color:transparent;
  transition:
    background-color var(--ds-duration-fast) var(--ds-ease-out),
    border-color var(--ds-duration-fast) var(--ds-ease-out),
    color var(--ds-duration-fast) var(--ds-ease-out),
    scale var(--ds-duration-fast) var(--ds-ease-out);
}
.ob-button:hover{background:var(--ob-button-bg-hover)}
.ob-button:active{scale:.97}
.ob-button:disabled,
.ob-button[aria-disabled="true"]{opacity:.5;cursor:not-allowed;scale:1}
.ob-button svg{display:block;flex:none}

.ob-button--secondary{
  --ob-button-bg:var(--ds-color-surface);
  --ob-button-bg-hover:var(--ds-color-surface-hover);
  --ob-button-fg:var(--ds-color-text);
  --ob-button-border:var(--ds-color-border-strong);
}
.ob-button--ghost{
  --ob-button-bg:transparent;
  --ob-button-bg-hover:var(--ds-color-surface-hover);
  --ob-button-fg:var(--ds-color-text);
}

.ob-button--sm{--ob-button-height:var(--ds-control-height-sm);--ob-button-pad:var(--ds-control-pad-x-sm)}
.ob-button--lg{--ob-button-height:var(--ds-control-height-lg);--ob-button-pad:var(--ds-control-pad-x-lg)}

/* Icon only. Give it an aria-label. */
.ob-button--icon{
  padding:0;
  width:max(var(--ob-button-height),var(--ds-control-tap-min));
  border-radius:var(--ds-radius-full);
}

/* While something is loading, the label keeps its width so the button
   doesn't change size, and an ob-inline-loading sits on top of it. */
.ob-button > .ob-inline-loading{display:none}
.ob-button[aria-busy="true"]{position:relative;cursor:progress}
.ob-button[aria-busy="true"] > :not(.ob-inline-loading){visibility:hidden}
.ob-button[aria-busy="true"] > .ob-inline-loading{display:inline-grid;position:absolute}

@media (prefers-reduced-motion:reduce){
  .ob-button:active{scale:1}
}

/* ob-chip. One of a row of options where one is on, like filters or a
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-chip{
  --ob-chip-radius:var(--ds-radius-full);
  --ob-chip-pad:var(--ds-space-xs) var(--ds-space-sm);
  display:inline-flex;align-items:center;justify-content:center;gap:var(--ds-space-2xs);
  padding:var(--ob-chip-pad);
  border:var(--ds-border-hairline) solid transparent;
  border-radius:var(--ob-chip-radius);
  background:transparent;
  color:var(--ds-color-text-muted);
  font:inherit;
  cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:
    color var(--ds-duration-fast) var(--ds-ease-out),
    background-color var(--ds-duration-base) var(--ds-ease-out),
    border-color var(--ds-duration-base) var(--ds-ease-out);
}
.ob-chip:hover{color:var(--ds-color-text);background:var(--ds-color-surface-hover)}

/* The accent is mixed into the background rather than used solid, so text
   on it keeps its contrast in every theme. */
.ob-chip[aria-pressed="true"],
.ob-chip.is-selected{
  color:var(--ds-color-text);
  background:color-mix(in srgb,var(--ds-color-accent) 20%,transparent);
  border-color:color-mix(in srgb,var(--ds-color-accent) 35%,transparent);
}

.ob-chip--outline{border-color:var(--ds-color-border)}
.ob-chip--outline:hover{border-color:var(--ds-color-border-strong)}
.ob-chip--outline[aria-pressed="true"],
.ob-chip--outline.is-selected{border-color:var(--ds-color-accent)}

/* ob-confetti. Gold confetti that pops off a button and settles on top of
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-confetti{
  --ob-confetti-count:90;
  --ob-confetti-room:220px;
  --ob-confetti-side:140px;
  --ob-confetti-gravity:1500;
  --ob-confetti-hold:1800ms;
  --ob-confetti-fade:700ms;
  /* The golds from gradient-text.css, so the two always match. Load that
     file too, or set these yourself. */
  --ob-confetti-dark:var(--ob-gold-dark);
  --ob-confetti-mid:var(--ob-gold);
  --ob-confetti-light:var(--ob-gold-light);
  position:relative;display:inline-block;
}
.ob-confetti > :not(.ob-confetti-canvas){position:relative;z-index:var(--ds-z-raised)}
.ob-confetti-canvas{
  position:absolute;left:0;top:0;z-index:calc(var(--ds-z-raised) + 1);
  pointer-events:none;
}

/* ob-field. A label, a control and a message, with the error state on the
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-field{
  --ob-field-error:var(--ds-color-danger);
  --ob-field-height:var(--ds-control-height-lg);
  --ob-field-hold:3000ms;
  display:flex;flex-direction:column;gap:var(--ds-space-2xs);
}

.ob-field-label{
  font-family:var(--ds-font-sans);
  font-size:var(--ds-type-label-size);
  font-weight:var(--ds-type-label-weight);
  line-height:var(--ds-type-label-leading);
  color:var(--ds-color-text);
}

.ob-input,
.ob-select{
  position:relative;
  display:flex;align-items:center;gap:var(--ds-space-2xs);
  min-height:var(--ob-field-height,var(--ds-control-height-lg));
  padding-right:var(--ds-space-2xs);
  background:var(--ds-color-surface);
  border:var(--ds-border-hairline) solid var(--ds-color-border-strong);
  border-radius:var(--ds-radius-md);
  transition:border-color var(--ds-duration-fast) var(--ds-ease-out),box-shadow var(--ds-duration-fast) var(--ds-ease-out);
}
.ob-input:hover,
.ob-select:hover{border-color:var(--ds-color-text-muted)}
.ob-input:focus-within,
.ob-select:focus-within{
  border-color:var(--ds-color-accent);
  box-shadow:0 0 0 1px var(--ds-color-accent);
}

/* 16px at least, or iOS zooms in on focus. */
.ob-input input,
.ob-select select{
  flex:1;min-width:0;align-self:stretch;
  padding:0 var(--ds-control-pad-x-md);
  border:0;background:transparent;outline:none;
  color:var(--ds-color-text);
  font-family:var(--ds-font-sans);
  font-size:max(16px,var(--ds-type-ui-size));
}
.ob-input input::placeholder{color:var(--ds-color-text-muted)}

/* The platform arrow can't take the text colour, so it goes and a chevron
   is drawn on the box instead. */
.ob-select select{-webkit-appearance:none;appearance:none;cursor:pointer}
.ob-select::after{
  content:"";flex:none;
  width:6px;height:6px;margin-right:var(--ds-space-sm);
  border-right:var(--ds-border-thick) solid var(--ds-color-text-muted);
  border-bottom:var(--ds-border-thick) solid var(--ds-color-text-muted);
  rotate:45deg;translate:0 -2px;
  pointer-events:none;
}

.ob-field-message{
  margin:0;
  font-family:var(--ds-font-sans);
  font-size:var(--ds-type-label-size);
  line-height:var(--ds-type-label-leading);
  color:var(--ds-color-text-muted);
}
/* An error message that's only there when something's wrong. */
.ob-field-message--error{display:none}

.ob-field.is-invalid .ob-input,
.ob-field.is-invalid .ob-select{border-color:var(--ob-field-error);box-shadow:0 0 0 1px var(--ob-field-error)}
.ob-field.is-invalid .ob-field-message--error{display:block;color:var(--ob-field-error)}
.ob-field.is-invalid .ob-field-message--error ~ .ob-field-message:not(.ob-field-message--error){display:none}

/* A short, damped side to side. shakeField() adds the class. */
.ob-input.is-shaking,
.ob-select.is-shaking{animation:ob-field-shake 380ms var(--ds-ease-out)}
@keyframes ob-field-shake{
  0%,100%{translate:0}
  18%{translate:-7px}
  38%{translate:6px}
  58%{translate:-4px}
  78%{translate:2px}
}
@media (prefers-reduced-motion:reduce){
  .ob-input.is-shaking,.ob-select.is-shaking{animation:none}
}

/* ob-gradient-text. Gold, with the light moving slowly across it. It's the
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

/* The golds are on :root so ob-confetti can use the same ones. */
:where(:root){
  --ob-gold-dark:#A87428;
  --ob-gold:#E3B04E;
  --ob-gold-light:#F7DE9A;
}
.ob-gradient-text{
  --ob-gold-drift:7000ms;
  background-image:linear-gradient(
    110deg,
    var(--ob-gold-dark) 0%,
    var(--ob-gold) 22%,
    var(--ob-gold-light) 38%,
    var(--ob-gold) 50%,
    var(--ob-gold-dark) 68%,
    var(--ob-gold) 84%,
    var(--ob-gold-light) 100%
  );
  background-size:220% 100%;
  -webkit-background-clip:text;background-clip:text;
  color:transparent;-webkit-text-fill-color:transparent;
  animation:ob-gold-drift var(--ob-gold-drift) ease-in-out infinite alternate;
}
@keyframes ob-gold-drift{
  from{background-position:0% 0}
  to{background-position:100% 0}
}
@media (prefers-reduced-motion:reduce){
  .ob-gradient-text{animation:none;background-position:30% 0}
}

/* ob-inline-loading. A small spinner that turns into a tick when the thing
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-inline-loading{
  --ob-loading-size:22px;
  --ob-loading-stroke:2.5;
  --ob-loading-spin:800ms;
  display:inline-grid;place-items:center;flex:none;
  width:var(--ob-loading-size);height:var(--ob-loading-size);
  color:currentColor;
}
.ob-inline-loading svg{display:block;width:100%;height:100%;overflow:visible}

.ob-inline-loading-track,
.ob-inline-loading-arc{fill:none;stroke:currentColor;stroke-width:var(--ob-loading-stroke)}
.ob-inline-loading-track{opacity:.2}
.ob-inline-loading-arc{
  stroke-linecap:round;
  stroke-dasharray:28 100;
  transform-box:fill-box;transform-origin:center;
  animation:ob-inline-loading-spin var(--ob-loading-spin) linear infinite;
  transition:opacity var(--ds-duration-fast) var(--ds-ease-out);
}
@keyframes ob-inline-loading-spin{to{rotate:360deg}}

.ob-inline-loading-disc{
  fill:var(--ds-color-success);
  transform-box:fill-box;transform-origin:center;scale:0;
  transition:scale var(--ds-duration-base) var(--ds-ease-spring);
}
.ob-inline-loading-tick{
  fill:none;stroke:var(--ds-color-accent-contrast);stroke-width:2.2;
  stroke-linecap:round;stroke-linejoin:round;
  stroke-dasharray:1;stroke-dashoffset:1;
  transition:stroke-dashoffset var(--ds-duration-slow) var(--ds-ease-out);
}

.ob-inline-loading[data-state="finished"] .ob-inline-loading-arc,
.ob-inline-loading[data-state="finished"] .ob-inline-loading-track{opacity:0}
.ob-inline-loading[data-state="finished"] .ob-inline-loading-disc{scale:1}
.ob-inline-loading[data-state="finished"] .ob-inline-loading-tick{
  stroke-dashoffset:0;
  transition-delay:var(--ds-duration-fast);
}

@media (prefers-reduced-motion:reduce){
  .ob-inline-loading-arc{animation-duration:2400ms}
  .ob-inline-loading-disc,.ob-inline-loading-tick{transition:none}
}

/* ob-link. Muted text with a fine underline, and both take the accent on
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-link{
  color:var(--ds-color-text-muted);
  text-decoration-line:underline;
  text-decoration-thickness:var(--ds-border-hairline);
  text-decoration-color:var(--ds-color-border-strong);
  text-underline-offset:.28em;
  transition:color var(--ds-duration-base) var(--ds-ease-out),text-decoration-color var(--ds-duration-base) var(--ds-ease-out);
}
.ob-link:hover{color:var(--ds-color-accent);text-decoration-color:currentColor}
.ob-link--strong{color:var(--ds-color-text)}

/* Gradient text has to stay transparent on hover or the flat colour covers
   the gold. The underline still changes. */
.ob-link.ob-gradient-text,
.ob-link.ob-gradient-text:hover{color:transparent;text-decoration-color:var(--ob-gold,currentColor)}

/* ob-loading-text. A line that says what's happening while you wait, with a
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-loading-text{
  --ob-loading-text-sweep:1800ms;
  --ob-loading-text-swap:180ms;
  --ob-loading-text-hold:1400ms;
  position:relative;display:inline-block;
  color:var(--ds-color-text-dim);
}
.ob-loading-text-sizer{display:block;visibility:hidden;white-space:nowrap;height:0}
.ob-loading-text-phrase{
  display:block;white-space:nowrap;
  -webkit-mask-image:linear-gradient(90deg,rgb(0 0 0 / .6) 35%,#000 50%,rgb(0 0 0 / .6) 65%);
  mask-image:linear-gradient(90deg,rgb(0 0 0 / .6) 35%,#000 50%,rgb(0 0 0 / .6) 65%);
  -webkit-mask-size:300% 100%;mask-size:300% 100%;
  animation:ob-loading-text-sweep var(--ob-loading-text-sweep) linear infinite;
  transition:opacity var(--ob-loading-text-swap) var(--ds-ease-out),translate var(--ob-loading-text-swap) var(--ds-ease-out);
}
@keyframes ob-loading-text-sweep{
  from{-webkit-mask-position:100% 0;mask-position:100% 0}
  to{-webkit-mask-position:0 0;mask-position:0 0}
}
.ob-loading-text-phrase.is-leaving{opacity:0;translate:0 -6px}
.ob-loading-text-phrase.is-arriving{opacity:0;translate:0 6px;transition:none}

@media (prefers-reduced-motion:reduce){
  .ob-loading-text-phrase{animation:none;-webkit-mask-image:none;mask-image:none;transition:none}
}

/* ob-reveal. Things that fade up as they scroll into view. revealOnScroll()
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-motion .ob-reveal{
  opacity:0;translate:0 12px;
  transition:opacity var(--ds-duration-slower) var(--ds-ease-out),translate var(--ds-duration-slower) var(--ds-ease-out);
}
.ob-motion .ob-reveal.is-visible{opacity:1;translate:0}

.ob-motion .ob-reveal-word{
  display:inline-block;opacity:0;translate:0 .4em;
  transition:opacity var(--ds-duration-slow) var(--ds-ease-out),translate var(--ds-duration-slow) var(--ds-ease-out);
}
.ob-motion .ob-reveal-word.is-visible{opacity:1;translate:0}
/* Punctuation stuck to the word before stays inline, so a line can't break
   in front of it. */
.ob-reveal-word--tail{display:inline}

@media (prefers-reduced-motion:reduce){
  .ob-motion .ob-reveal,.ob-motion .ob-reveal-word{opacity:1;translate:0;transition:none}
}

/* ob-spinner. 8 dots in a ring, a line of text tall, with a bright one
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-spinner{
  --ob-spinner-size:16px;
  --ob-spinner-dot:2.5px;
  --ob-spinner-lap:900ms;
  position:relative;display:inline-block;flex:none;
  width:var(--ob-spinner-size);height:var(--ob-spinner-size);
  color:var(--ds-color-text-muted);
}
.ob-spinner i{
  position:absolute;top:50%;left:50%;
  width:var(--ob-spinner-dot);height:var(--ob-spinner-dot);
  margin:calc(var(--ob-spinner-dot) / -2);
  border-radius:var(--ds-radius-full);
  background:currentColor;
  transform:rotate(calc(var(--i) * 45deg)) translateY(calc(var(--ob-spinner-size) / -2 + var(--ob-spinner-dot) / 2));
  opacity:.25;
  animation:ob-spinner-chase var(--ob-spinner-lap) linear infinite;
  animation-delay:calc(var(--ob-spinner-lap) / 8 * var(--i) - var(--ob-spinner-lap));
}
.ob-spinner i:nth-child(1){--i:0}
.ob-spinner i:nth-child(2){--i:1}
.ob-spinner i:nth-child(3){--i:2}
.ob-spinner i:nth-child(4){--i:3}
.ob-spinner i:nth-child(5){--i:4}
.ob-spinner i:nth-child(6){--i:5}
.ob-spinner i:nth-child(7){--i:6}
.ob-spinner i:nth-child(8){--i:7}
@keyframes ob-spinner-chase{
  0%{opacity:1}
  70%,100%{opacity:.25}
}
@media (prefers-reduced-motion:reduce){
  .ob-spinner i{animation-duration:calc(var(--ob-spinner-lap) * 3)}
}

/* ob-stagger. The children of something that's just opened, arriving one
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-stagger{--ob-stagger-step:45ms}
.ob-stagger > *{
  opacity:0;translate:0 8px;
  transition:opacity var(--ds-duration-base) var(--ds-ease-out),translate var(--ds-duration-base) var(--ds-ease-out);
}
.ob-stagger.is-open > *{opacity:1;translate:0;transition-delay:calc(var(--ob-stagger-step) * var(--ob-stagger-i,0))}
.ob-stagger > :nth-child(2){--ob-stagger-i:1}
.ob-stagger > :nth-child(3){--ob-stagger-i:2}
.ob-stagger > :nth-child(4){--ob-stagger-i:3}
.ob-stagger > :nth-child(5){--ob-stagger-i:4}
.ob-stagger > :nth-child(6){--ob-stagger-i:5}
.ob-stagger > :nth-child(n+7){--ob-stagger-i:6}

@media (prefers-reduced-motion:reduce){
  .ob-stagger > *{transition:none}
}

/* ob-tooltip. A short label that shows on hover or keyboard focus.
 * Generated by obvious v0.1.0. Do not edit by hand:
 * change src/ and re-run `npm run build`.
 */

.ob-tooltip-host{position:relative;display:inline-flex}
.ob-tooltip{
  position:absolute;bottom:calc(100% + var(--ds-space-xs));left:50%;
  z-index:var(--ds-z-overlay);
  translate:-50% 4px;
  padding:var(--ds-space-2xs) var(--ds-space-xs);
  border-radius:var(--ds-radius-sm);
  background:var(--ds-color-text);
  color:var(--ds-color-text-inverse);
  font-family:var(--ds-font-sans);
  font-size:var(--ds-type-label-size);
  line-height:var(--ds-type-label-leading);
  white-space:nowrap;
  box-shadow:var(--ds-shadow-md);
  opacity:0;pointer-events:none;
  transition:opacity var(--ds-duration-fast) var(--ds-ease-out),translate var(--ds-duration-fast) var(--ds-ease-out);
}
.ob-tooltip--start{left:0;translate:0 4px}
.ob-tooltip--end{left:auto;right:0;translate:0 4px}

.ob-tooltip-host:hover > .ob-tooltip,
.ob-tooltip-host:focus-within > .ob-tooltip{
  opacity:1;translate:-50% 0;
  transition-delay:var(--ds-duration-instant);
}
.ob-tooltip-host:hover > .ob-tooltip--start,
.ob-tooltip-host:focus-within > .ob-tooltip--start,
.ob-tooltip-host:hover > .ob-tooltip--end,
.ob-tooltip-host:focus-within > .ob-tooltip--end{translate:0 0}

@media (prefers-reduced-motion:reduce){
  .ob-tooltip{transition:none}
}
