/*
 * Buffalo147 AI — base layer
 *
 * Reset, document defaults, and the handful of components that appear on
 * every screen. Page-specific styles live in their own files and are only
 * loaded on the pages that need them.
 */

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

/*
 * The hidden attribute must always win.
 *
 * The browser hides [hidden] with a user-agent rule of the lowest possible
 * specificity, so any class that sets `display` silently defeats it. That is
 * how the delete dialog ended up on screen the moment the chat page loaded,
 * with its own cancel button unable to close it: the JavaScript was setting
 * the attribute correctly and `.bf-modal { display: flex }` kept overriding
 * it. Declaring it here removes the whole category of bug rather than
 * patching the two places it happened to surface.
 */
[hidden] { display: none !important; }

/*
 * Anything pressable shows a pointer.
 *
 * Said once, here, rather than remembered in every rule that styles a
 * control. A button that works but keeps the arrow reads as dead space
 * — which is how it gets reported, every time.
 *
 * Disabled things are the exception: they should look unavailable.
 */
button:not([disabled]),
summary,
a[href],
label[for],
[role="option"]:not([disabled]),
[role="tab"]:not([disabled]) { cursor: pointer; }

button[disabled],
[role="option"][disabled] { cursor: not-allowed; }

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-color: var(--bf-line-strong) transparent;
}

body {
  margin: 0;
  background: var(--bf-bg);
  color: var(--bf-text);
  font-family: var(--bf-font);
  font-size: var(--bf-size-base);
  line-height: var(--bf-leading);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/*
 * Form controls do not inherit a font.
 *
 * Browsers give <button>, <input>, <select> and <textarea> the operating
 * system's font rather than the page's, and that has to be undone
 * explicitly. Every rule in this project that remembered to say
 * `font-family: inherit` was working around this one missing line, and
 * every rule that forgot rendered Persian in whatever the OS supplies —
 * which is exactly where the type looked wrong.
 *
 * Fixed once, here, so no future control can be caught by it.
 */
button,
input,
select,
textarea,
optgroup {
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
}


h1, h2, h3, h4 {
  margin: 0 0 var(--bf-space-3);
  line-height: var(--bf-leading-tight);
  font-weight: 700;
}

h1 { font-size: var(--bf-size-3xl); }
h2 { font-size: var(--bf-size-2xl); }
h3 { font-size: var(--bf-size-xl); }

p { margin: 0 0 var(--bf-space-4); }

a {
  color: var(--bf-accent);
  text-decoration: none;
  transition: color var(--bf-fast) var(--bf-ease);
}
a:hover { color: var(--bf-accent-hover); }

img, svg, video { max-width: 100%; height: auto; display: block; }

/* Latin runs inside Persian text — model names, code, emails — read better
   in Inter than in Vazirmatn's Latin fallback. */
code, kbd, samp, pre, .bf-ltr {
  font-family: var(--bf-font-mono);
  direction: ltr;
  unicode-bidi: plaintext;
}

::selection {
  background: var(--bf-accent-soft);
  color: var(--bf-text);
}

/* Keyboard focus must always be visible. Mouse users never see it. */
:focus-visible {
  outline: 2px solid var(--bf-accent);
  outline-offset: 2px;
  border-radius: var(--bf-radius-xs);
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--bf-line-strong);
  border-radius: var(--bf-radius-pill);
  border: 3px solid var(--bf-bg);
}
::-webkit-scrollbar-thumb:hover { background: #4a4a4a; }

/* ---------------------------------------------------------------------
 * Utilities
 * ------------------------------------------------------------------ */

.bf-container {
  width: 100%;
  max-width: var(--bf-container);
  margin-inline: auto;
  padding-inline: var(--bf-space-5);
}

.bf-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

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

.bf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--bf-space-2);
  padding: 12px 22px;
  border: 1px solid transparent;
  border-radius: var(--bf-radius-pill);
  background: var(--bf-glass);
  color: var(--bf-text);
  font-family: inherit;
  font-size: var(--bf-size-md);
  font-weight: 500;
  line-height: 1;
  cursor: pointer;
  transition: background var(--bf-fast) var(--bf-ease),
              border-color var(--bf-fast) var(--bf-ease),
              box-shadow var(--bf-normal) var(--bf-ease),
              transform var(--bf-fast) var(--bf-ease);
  user-select: none;
  white-space: nowrap;
}

.bf-btn:active { transform: translateY(1px); }

.bf-btn[disabled],
.bf-btn[aria-busy='true'] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

/*
 * One flat orange, with nothing laid over it.
 *
 * There were three things making it uneven: a white wash across the
 * crown, a gradient darkening toward the foot, and two inset highlights
 * on the edges. Together they read as a sheen lying over the colour
 * rather than as the colour itself — the button looked lit rather than
 * coloured.
 *
 * The glow underneath went with them: it cast the accent onto whatever
 * sat behind the button, which on a dark page is a smudge.
 */
.bf-btn--primary {
  background: #A94F31;
  border: 1px solid #A94F31;
  box-shadow: none;
  color: #FFFFFF;
}

/*
 * Under the pointer the colour lifts; no film is laid over it.
 *
 * An eighteen percent white wash across the top read as a pale sheet
 * covering the button rather than as the button answering, and the glow
 * spread that colour across whatever sat beneath. The rail has answered
 * a pointer with nothing but a small change of surface since the
 * beginning, and every control that has been corrected since has been
 * brought to match it. This is the last one.
 */
/*
 * The label keeps its colour when the pointer arrives.
 *
 * These buttons are links, and `a:hover` takes every link to
 * `--bf-accent-hover` — which is the very colour the button's own hover
 * fills the background with. Measured in the browser: text and
 * background both came out rgb(183, 107, 82), and the words vanished
 * into the button.
 *
 * A rule for the whole document reaching inside a component is the kind
 * of thing only the browser can tell you; the stylesheet on its own says
 * white, and means it.
 */
.bf-btn--primary:hover:not([disabled]),
.bf-btn--primary:hover:not([disabled]) .bf-btn__label {
  background: var(--orange-400);
  color: #FFFFFF;
  box-shadow: none;
}

.bf-btn:hover:not([disabled]),
.bf-btn:hover:not([disabled]) .bf-btn__label {
  color: inherit;
}

.bf-btn--primary:hover:not([disabled]) {
  color: #FFFFFF;
}
.bf-btn--primary:active:not([disabled]) { background: var(--bf-accent-press); }

.bf-btn--ghost {
  background: var(--bf-glass);
  border-color: var(--bf-glass-border);
  color: var(--bf-text);
}
.bf-btn--ghost:hover:not([disabled]) {
  background: var(--bf-glass-strong);
  border-color: var(--bf-line-strong);
}

.bf-btn--block { width: 100%; }

/* Spinner shown while a button is waiting on the server. */
.bf-btn__spinner {
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: bf-spin 640ms linear infinite;
  display: none;
}
.bf-btn[aria-busy='true'] .bf-btn__spinner { display: block; }
.bf-btn[aria-busy='true'] .bf-btn__label { opacity: 0.7; }

@keyframes bf-spin { to { transform: rotate(360deg); } }

/* ---------------------------------------------------------------------
 * Fields
 * ------------------------------------------------------------------ */

.bf-field { margin-bottom: var(--bf-space-4); }

.bf-field__label {
  display: block;
  margin-bottom: var(--bf-space-2);
  font-size: var(--bf-size-sm);
  font-weight: 500;
  color: var(--bf-text-muted);
}

.bf-field__wrap { position: relative; display: block; }

.bf-input {
  width: 100%;
  padding: 13px 16px;
  background: var(--bf-surface-2);
  border: 1px solid var(--bf-line);
  border-radius: var(--bf-radius-md);
  color: var(--bf-text);
  font-family: inherit;
  font-size: var(--bf-size-md);
  transition: border-color var(--bf-fast) var(--bf-ease),
              background var(--bf-fast) var(--bf-ease),
              box-shadow var(--bf-fast) var(--bf-ease);
}

.bf-input::placeholder { color: var(--bf-text-faint); }

/*
 * A focused field is outlined, not tinted.
 *
 * The three-pixel warm halo washed over the field itself, so the one
 * being typed in came out #231A17 against #151515 for the one beside it
 * — two fields in a row, two colours, for no reason the reader can see.
 * A single accent border says the same thing without changing the
 * surface.
 */
.bf-input:focus {
  outline: none;
  background: var(--bf-surface-3);
  border-color: var(--orange-500);
  box-shadow: none;
}

/* Email and password are Latin by nature; force ltr so the caret and any
   punctuation behave the way people expect. */
.bf-input--ltr { direction: ltr; text-align: left; }

.bf-field--error .bf-input {
  border-color: var(--bf-danger);
  box-shadow: 0 0 0 3px var(--bf-danger-soft);
}

.bf-field__error {
  display: none;
  margin-top: var(--bf-space-2);
  font-size: var(--bf-size-xs);
  color: var(--bf-danger);
}
.bf-field--error .bf-field__error { display: block; }

/* Reveal toggle sits on the far edge of the password field. */
.bf-reveal {
  position: absolute;
  inset-inline-start: 10px;
  inset-block-start: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px; height: 32px;
  padding: 0;
  background: none;
  border: 0;
  border-radius: var(--bf-radius-xs);
  color: var(--bf-text-dim);
  cursor: pointer;
  transition: color var(--bf-fast) var(--bf-ease);
}
.bf-reveal:hover { color: var(--bf-text); }

.bf-checkbox {
  display: flex;
  align-items: flex-start;
  gap: var(--bf-space-2);
  font-size: var(--bf-size-sm);
  color: var(--bf-text-muted);
  cursor: pointer;
  user-select: none;
}
.bf-checkbox input {
  width: 17px; height: 17px;
  margin: 4px 0 0;
  accent-color: var(--bf-accent);
  flex: none;
  cursor: pointer;
}

/* ---------------------------------------------------------------------
 * Cards and notices
 * ------------------------------------------------------------------ */

.bf-card {
  background: var(--bf-surface);
  border: 1px solid var(--bf-line);
  border-radius: var(--bf-radius);
  box-shadow: var(--bf-shadow);
}

.bf-notice {
  display: none;
  align-items: flex-start;
  gap: var(--bf-space-2);
  padding: 12px 16px;
  margin-bottom: var(--bf-space-4);
  border: 1px solid transparent;
  border-radius: var(--bf-radius-md);
  font-size: var(--bf-size-sm);
}
.bf-notice[data-visible='1'] { display: flex; }

.bf-notice--error   { background: var(--bf-danger-soft);  border-color: rgba(255, 82, 82, 0.3);  color: #FF9E9E; }
.bf-notice--success { background: var(--bf-success-soft); border-color: rgba(52, 208, 127, 0.3); color: #7BE8B4; }
.bf-notice--info    { background: var(--bf-info-soft);    border-color: rgba(77, 163, 255, 0.3); color: #9FCBFF; }

/* =====================================================================
 * Small screens
 *
 * These rules are global because the mistakes they fix are global: text
 * sized for a desktop column, tap targets sized for a mouse pointer, and
 * layouts that assume the viewport is wider than the content.
 *
 * Written mobile-last rather than mobile-first because the desktop rules
 * came first; that is a compromise, but scattering overrides through five
 * files would be worse.
 * ================================================================== */

@media (max-width: 640px) {

  /* Headings sized for a 1200px column are shouting on a 375px screen. */
  :root {
    --bf-size-4xl: 30px;
    --bf-size-3xl: 25px;
    --bf-size-2xl: 21px;
    --bf-size-xl:  18px;
  }

  .bf-container { padding-inline: var(--bf-space-4); }

  /* Anything meant to be tapped needs a finger-sized target. Below about
     44px people miss, and missing a delete button is worse than missing
     anything else. */
  .bf-btn,
  .bf-input,
  .bf-icon-btn,
  .bf-input__btn { min-height: 44px; }

  .bf-btn { width: 100%; }
  .bf-btn--sm { width: auto; min-height: 38px; }

  /* Long model names and URLs must wrap rather than widen the page. */
  code, .bf-ltr { word-break: break-word; overflow-wrap: anywhere; }
}

/* Notched and gesture-bar phones: keep content out from under the hardware. */
@supports (padding: max(0px)) {
  body {
    padding-inline: max(0px, env(safe-area-inset-left)) max(0px, env(safe-area-inset-right));
  }
}

/* A table is the one layout that genuinely cannot reflow. Let it scroll
   sideways inside its own box instead of stretching the whole page. */
@media (max-width: 780px) {
  .bf-scroll-x {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* =====================================================================
 * The invitation for a visitor without an account
 *
 * It sits in the workspace, above the thing they were about to press,
 * rather than at the door. Someone who has reached this point has an
 * intention — a prompt typed, a model chosen — and a modal would lose it.
 *
 * It reads as a note, not as an error: the accent is on the button only,
 * and nothing about it is red.
 * ================================================================== */

.bf-gate {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin: 0 0 14px;
  padding: 16px 18px;
  border: 1px solid rgba(169, 79, 49, .32);
  border-radius: 14px;
  background: rgba(169, 79, 49, .07);
}

.bf-gate__mark {
  display: grid;
  place-items: center;
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: 11px;
  background: rgba(169, 79, 49, .14);
  color: var(--orange-400);
}

.bf-gate__mark svg {
  width: 21px;
  height: 21px;
}

.bf-gate__text {
  flex: 1 1 240px;
  margin: 0;
  color: var(--bf-text);
  font-size: var(--bf-size-base);
  font-weight: 600;
  line-height: 1.8;
}

.bf-gate__text span {
  display: block;
  color: var(--bf-text-muted);
  font-size: var(--bf-size-sm);
  font-weight: 400;
}

.bf-gate__acts {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  flex: none;
}

.bf-gate__go {
  display: inline-flex;
  align-items: center;
  padding: 11px 22px;
  border: 1px solid var(--orange-600);
  border-radius: 10px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.035), rgba(255,255,255,0) 34%),
    var(--orange-500);
  color: var(--bf-on-accent);
  font-size: var(--bf-size-sm);
  font-weight: 600;
  text-decoration: none;
  transition: var(--bf-ease-all);
}

.bf-gate__go:hover {
  background: var(--orange-400);
  border-color: var(--orange-500);
}

.bf-gate__in {
  color: var(--bf-text-muted);
  font-size: var(--bf-size-sm);
  text-decoration: none;
  transition: var(--bf-ease-all);
}

.bf-gate__in:hover {
  color: var(--orange-400);
}

@media (max-width: 620px) {
  .bf-gate { flex-direction: column; align-items: stretch; text-align: center; }
  .bf-gate__mark { margin-inline: auto; }
  .bf-gate__acts { justify-content: center; }
  .bf-gate__go { justify-content: center; flex: 1; }
}

/* =====================================================================
 * The model's mark
 * =====================================================================
 *
 * WHY THESE RULES ARE HERE AND NOT IN models.css.
 *
 * They were in models.css, which the catalogue and the home page load and
 * the STUDIO PAGES DO NOT: /image and /video take base, chat and image.
 * So on those two pages `.bf-model-logo` had no size, no tile and no
 * padding — it was styled only where some other selector happened to
 * reach it, which is the composer's trigger and the open model menu.
 *
 * Everywhere else on those pages the mark rendered at the image file's
 * own size. In the style filter that meant a full-resolution logo inside
 * a 32px circle, which is what the owner photographed and sent.
 *
 * `bf_model_logo()` prints this markup from one function, so its rules
 * belong in the stylesheet every page loads. Anything that wants a
 * different size for its own context still overrides it, as the picker
 * and the menu already do.
 */
/*
 * The logo tile.
 *
 * This is the base every logo inherits, in the picker button, the picker
 * rows, the model cards and the model page. Changed here rather than at
 * one of those sites, because fixing it in one place is how the others get
 * left behind — which is exactly what happened the first time.
 *
 * Why a tile at all: vendor marks are drawn for whatever background their
 * owner had in mind, and several are solid white. Dropped straight onto a
 * dark page, one of them is a glaring rectangle. Seating each mark on its
 * own tinted tile makes every one of them read correctly, and keeps a row
 * of them even, because the tiles match even when the marks do not.
 *
 * The tint is the vendor's own colour at low opacity: enough to seat a
 * white mark, never enough to compete with the page.
 */
.bf-model-logo {
  width: 46px; height: 46px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 13px;
  overflow: hidden;
  /*
   * Barely there.
   *
   * With a transparent mark — which is what to use — the tile should read
   * as part of the page, not as a coloured badge sitting on it. So it is
   * the page's own surface with a whisper of the brand in it: enough to
   * separate one row from the next, not enough to be noticed.
   *
   * The stronger tint the first version used made every logo look like it
   * had been given a highlight, which is a different problem from the
   * white square it was meant to solve.
   */
  background: color-mix(in srgb, var(--logo-accent, #A94F31) 7%, var(--bf-surface-3));
  border: 1px solid color-mix(in srgb, var(--logo-accent, #A94F31) 14%, var(--bf-line));
}

/* Padding so a mark never touches the edge of its tile, and never fills
   it corner to corner — that is what made a white square of a logo. */
.bf-model-logo img {
  width: 74%;
  height: 74%;
  object-fit: contain;
  padding: 0;
}



/* Older browsers still get a readable tile rather than a broken one. */
@supports not (background: color-mix(in srgb, red 10%, transparent)) {
  .bf-model-logo {
    background: var(--bf-surface-3);
    border-color: var(--bf-line);
  }
}

/* Shown until the owner uploads the vendor's official logo. */
.bf-model-logo--fallback {
  background: color-mix(in srgb, var(--logo-accent, #A94F31) 16%, transparent);
  border-color: color-mix(in srgb, var(--logo-accent, #A94F31) 34%, transparent);
  color: var(--logo-accent, #A94F31);
  font-weight: 700;
  font-size: 15px;
  letter-spacing: .02em;
}

/*
 * A logo that brings its own background.
 *
 * Nothing can strip a white rectangle out of a PNG, so instead of fighting
 * it the tile becomes the same white and the mark fills it edge to edge.
 * The seam disappears, the rounded corner is kept by the tile, and what
 * was a glaring block becomes a deliberate white chip.
 */
.bf-model-logo--boxed {
  background: #fff;
  border-color: rgba(255, 255, 255, .18);
}

.bf-model-logo--boxed img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/*
 * SVG marks are placed inline, not in an <img>.
 *
 * A mark shipped as a single path with `fill="currentColor"` — several are,
 * so they can be recoloured — comes out black when loaded through an
 * <img>, because there is no colour to resolve against. That is what
 * happened to the GPT mark while Claude's, which carries its own fill, was
 * fine. CSS cannot reach inside an <img>, so no rule can correct it.
 *
 * Inlining the file makes it part of the page, and then it inherits.
 * A mark with its own colours is unaffected, because it never asks for one.
 */
.bf-model-logo svg {
  width: 74%;
  height: 74%;
  color: var(--logo-accent, #A94F31);
}
