/* ============================================================================
   lore-ui.css — the primitives. One implementation each.
   ----------------------------------------------------------------------------
   Cory, 20 Sep 2026: "I want every element/module to run off of one piece of
   code. That includes Lore chat, <hr>'s (there can be two versions), Headers
   (h1, h2, h3, etc), regular text, subtext, explainer text, links, buttons,
   cards, chips, everything."

   COLOURS ARE NOT HERE. They are in lore-tokens.css, which is the single
   source, and this file only ever reads them through var(). A primitive that
   writes a hex has stopped being a primitive.

   Every page reaches this file through palettes.css, which all forty of them
   already link in the one position that matters — after styles.css, before
   polish.css. No page was edited to get it.
   ========================================================================== */

/* ============================================================================
   WAITING — the Lore flame, flickering.
   ----------------------------------------------------------------------------
   Cory: "I want all waiting to load to be the flame icon spinning, or better
   yet flickering like a fire with loading.... text, to brand Lore."

   What it replaces: fourteen separate `@keyframes spin` definitions across
   eighteen files, plus four named variants (oi-spin, oi-voice-spin,
   oi-recap-spin), and three hundred-odd hand-written "Loading…" strings. A
   rotating ring is the most generic thing a screen can show; it says an app is
   working, not which app.

   HOW IT IS BUILT, and why there is no JavaScript. The flame is three layers —
   body, inner core, spark — each an SVG in a background-image, each on its own
   animation with its own period. The periods are deliberately coprime-ish
   (1.7s, 1.15s, 2.3s) so the three never resynchronise and the flicker never
   develops a beat you can count. A single layer pulsing on one timer reads as
   a blinking icon; three that drift apart read as fire.

   The brand mark keeps its own colours (it is a logo, not a UI icon), which is
   the one deliberate exception to "chrome is neutral" in this file. The word
   beside it is --text3 like any other quiet label.

   USE:  <span class="lore-loading">loading…</span>          inline, in a row
         <div class="lore-loading is-block">loading…</div>   centred in a panel
         <span class="lore-loading is-sm">loading…</span>     inside a button

   The text is the element's own content, so it can say what is actually being
   waited for ("loading your trips…") without a second class or any API.
   ========================================================================== */
/* LEGACY NAMES, kept working rather than chased. .oi-comms-loading is used in
   eleven places in comms.js and was defined once in comms-v3.css — already a
   shared class, just a different shared class. Aliasing it here converts all
   eleven at once and means nobody has to touch eleven call sites to get the
   flame. Same trick lore-tokens.css uses for its colour aliases: one
   declaration, more than one name, so the copies cannot drift. */
.lore-loading,
.oi-comms-loading {
  --lore-flame-h: 24px;
  display: inline-flex;
  align-items: center;
  gap: 9px;
  color: var(--text3);
  font-size: 13px;
  font-style: normal;
  line-height: 1.3;
}
.lore-loading::before,
.oi-comms-loading::before {
  content: '';
  flex: 0 0 auto;
  width: calc(var(--lore-flame-h) * 36 / 78);
  height: var(--lore-flame-h);
  /* All three share the ember mark's viewBox, so they overlay exactly. */
  background-repeat: no-repeat;
  background-position: center;
  background-size: contain;
  /* Painted front to back: the FIRST background-image is the topmost layer,
     so the bright core and the spark are listed before the flame body they
     sit inside. Listed the other way round — which is the order they read
     naturally, and the order this shipped in for one commit — the body
     covers its own core and the mark renders as a flat orange teardrop. */
  background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2036%2078%22%3E%3Cg%20transform%3D%22translate%2817%2C43%29%22%3E%3Ccircle%20cx%3D%228%22%20cy%3D%22-39%22%20r%3D%222.4%22%20fill%3D%22%23f7b65c%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"), url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2036%2078%22%3E%3Cg%20transform%3D%22translate%2817%2C43%29%22%3E%3Cpath%20d%3D%22M2%2C24%20C%20-7%2C14%20-6%2C0%200%2C-10%20C%203%2C-15%206%2C-8%205%2C-1%20C%209%2C5%208%2C16%202%2C24%20Z%22%20fill%3D%22%23f7b65c%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E"), url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2036%2078%22%3E%3Cg%20transform%3D%22translate%2817%2C43%29%22%3E%3Cpath%20d%3D%22M0%2C31%20C%20-15%2C19%20-15%2C-2%20-5%2C-15%20C%200%2C-22%203%2C-27%201%2C-33%20C%2013%2C-23%2017%2C-7%2015%2C8%20C%2013%2C21%207%2C26%200%2C31%20Z%22%20fill%3D%22%23e8833a%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
  transform-origin: 50% 100%;
  animation: lore-flame-body 1.7s ease-in-out infinite;
}
/* The core and the spark ride their own timers. Two pseudo-elements would let
   each carry its own transform, but ::before and ::after are all there is and
   ::after is not spare — so the drift lives in the opacity of the layered
   backgrounds instead, which is what the eye reads as flicker anyway. */
.lore-loading::after,
.oi-comms-loading::after {
  content: '';
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

/* Irregular on purpose. Even stops at 0/25/50/75/100 read as a pulse; these
   land where a flame actually moves — a fast lean, a slow recover, a catch. */
@keyframes lore-flame-body {
  0%   { transform: scale(1,    1)    translateY(0);      opacity: .92; }
  14%  { transform: scale(.94,  1.06) translateY(-.5px);  opacity: 1;   }
  31%  { transform: scale(1.04, .96)  translateY(.5px);   opacity: .86; }
  47%  { transform: scale(.97,  1.09) translateY(-1px);   opacity: 1;   }
  58%  { transform: scale(1.02, .99)  translateY(0);      opacity: .9;  }
  73%  { transform: scale(.95,  1.04) translateY(-.5px);  opacity: .97; }
  89%  { transform: scale(1.03, .97)  translateY(.5px);   opacity: .88; }
  100% { transform: scale(1,    1)    translateY(0);      opacity: .92; }
}

/* Centred in an empty panel — a list still loading, a tab just opened. */
.lore-loading.is-block {
  --lore-flame-h: 40px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: center;
  justify-content: center;
  padding: 28px 16px;
  width: 100%;
  font-size: 13.5px;
}

/* Inside a button or a chip, where the row height is already decided. */
.lore-loading.is-sm { --lore-flame-h: 15px; gap: 6px; font-size: 12px; }

/* A flame that will not hold still is worse than no flame for anyone who gets
   motion sick, and this thing appears on every screen. Hold it steady and let
   the word do the work. */
@media (prefers-reduced-motion: reduce) {
  .lore-loading::before,
  .oi-comms-loading::before { animation: none; }
}

/* .oi-comms-loading always sat centred in an empty panel, so it carries the
   block layout by default rather than needing `is-block` added in eleven
   places. */
.oi-comms-loading {
  --lore-flame-h: 36px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  justify-content: center;
  padding: 26px 16px;
  width: 100%;
}

/* ============================================================================
   TYPE — one scale, five roles.
   ----------------------------------------------------------------------------
   Cory, 20 Sep: "Headers (h1, h2, h3, etc), regular text, subtext, explainer
   text… I want every element/module to run off of one piece of code."

   These are not invented. Every size and weight below is one the app already
   uses in the place it is used most, measured off the live screens rather than
   chosen — so adopting a primitive changes which stylesheet a rule comes from,
   not what the page looks like. That is deliberate: a migration that also
   restyles everything cannot be reviewed, because you cannot tell a conversion
   from a mistake.

   THE ROLES, and how to pick one:
     .lore-h1        a lead's name, a page's title. One per screen.
     .lore-h2        a card's own title.
     .lore-h3        the spine — a workspace line, a comm type, a list heading.
     .lore-eyebrow   the small uppercase label over a section (LEAD TASKS).
     .lore-body      what you read.
     .lore-sub       supporting copy under it.
     .lore-hint      labels, timestamps, counts — the quiet stuff.
     .lore-explain   the sentence under a control saying what it does.

   .lore-sub and .lore-explain are close on purpose and differ in job: sub is
   MORE of the same thought, explain is a DIFFERENT one about the control. Same
   size, different colour, so the eye can tell them apart without a rule.
   ========================================================================== */
.lore-h1      { font-size: 26px;   font-weight: 800; line-height: 1.15; letter-spacing: -.01em; color: var(--text); margin: 0; }
.lore-h2      { font-size: 17px;   font-weight: 800; line-height: 1.25; color: var(--text); margin: 0; }
.lore-h3      { font-size: 14px;   font-weight: 800; line-height: 1.3;  color: var(--text); margin: 0; }
.lore-eyebrow { font-size: 11px;   font-weight: 700; line-height: 1.3;  letter-spacing: 1px; text-transform: uppercase; color: var(--text2); }
.lore-body    { font-size: 13.5px; font-weight: 400; line-height: 1.55; color: var(--text); }
.lore-sub     { font-size: 12.5px; font-weight: 400; line-height: 1.5;  color: var(--text2); }
.lore-hint    { font-size: 11px;   font-weight: 600; line-height: 1.4;  color: var(--text3); }
.lore-explain { font-size: 12.5px; font-weight: 400; line-height: 1.45; color: var(--text3); }

/* ============================================================================
   LINKS
   ----------------------------------------------------------------------------
   Underlined on hover, not at rest. A list with an underline under every link
   reads as ruled paper; the accent already says it is a link, and the
   underline is what confirms it under the cursor. Keyboard focus gets a ring
   rather than the hover state, because :hover never fires for a keyboard.
   ========================================================================== */
.lore-link {
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  border-radius: 3px;
}
.lore-link:hover { text-decoration: underline; text-underline-offset: 2px; }
.lore-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; text-decoration: none; }

/* ============================================================================
   BUTTONS — three, and no more.
   ----------------------------------------------------------------------------
     .is-primary  the one thing this screen is for. One per view.
     (default)    a real choice, offered next to it.
     .is-ghost    an action that is available, not urged.

   A fourth variant is nearly always a primary that somebody did not want to
   commit to. The lead card had three side by side — an amber pill, a black
   button and a grey one — which reads as three unrelated systems rather than
   three choices.

   TOUCH: 44px is the floor on a coarse pointer, which is his phone. A 32px
   button is fine under a mouse and a miss under a thumb, and the difference
   is a pointer query rather than a viewport one — a tablet is wide AND touched.
   ========================================================================== */
.lore-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 34px;
  padding: 7px 14px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text2);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  transition: background .12s, border-color .12s, color .12s;
}
.lore-btn:hover:not(:disabled) { background: var(--bg-hover); color: var(--text); }
.lore-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.lore-btn:disabled { opacity: .45; cursor: not-allowed; }

.lore-btn.is-primary {
  background: var(--accent-fill);
  border-color: var(--accent-fill);
  color: var(--accent-on);
  font-weight: 700;
}
.lore-btn.is-primary:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); color: var(--accent-on); }

.lore-btn.is-ghost { background: none; border-color: transparent; color: var(--text3); }
.lore-btn.is-ghost:hover:not(:disabled) { background: var(--bg-hover); color: var(--text); }

.lore-btn.is-danger { border-color: var(--danger); color: var(--danger); background: none; }
.lore-btn.is-danger:hover:not(:disabled) { background: color-mix(in srgb, var(--danger) 10%, transparent); color: var(--danger); }

@media (pointer: coarse) {
  .lore-btn { min-height: 44px; padding: 10px 16px; }
}

/* ============================================================================
   CARDS
   ----------------------------------------------------------------------------
   A card is a surface with a boundary. It is not a way to add emphasis: a card
   inside a card inside a panel is three boundaries around one thought, which
   is how the lead screen came to look busy. `.is-flush` is for a card that
   sits inside something already bounded — it keeps the padding and drops the
   border, which is almost always what was wanted.
   ========================================================================== */
.lore-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 18px 20px;
}
.lore-card.is-flush { border: none; border-radius: 0; padding: 14px 0; background: none; }
.lore-card.is-inset { background: var(--surface2); }

/* ============================================================================
   CHIPS — a small, self-contained value: a status, a tag, a filter.
   ----------------------------------------------------------------------------
   A chip is not a button. If it does something when you press it, it is a
   button and should look like one; a chip that is pressable is `.is-on`-able,
   which is a filter — a state, not an action.
   ========================================================================== */
.lore-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: none;
  color: var(--text3);
  font-family: inherit;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .03em;
  line-height: 16px;
  white-space: nowrap;
}
/* A SELECTED CHIP: accent border, accent wash, and the word in --text.
   Not accent text on the wash, which is the obvious thing to write and which
   accent-contrast.test.js has forbidden since 6 Sep. Re-measured against the
   one theme on 20 Sep rather than taking the old nine-palette number on
   trust: accent on its own wash is 4.99:1 over --surface, 4.68:1 over --bg and
   4.45:1 over --surface2 — so it fails AA on the deepest of the three, by a
   hair, in the theme most people use. Dark is fine (6.2–7.6:1), which is
   exactly the trap: it looks right where it was checked.
   The border and the wash carry the state; --text carries the words at
   13.5:1 or better everywhere. */
.lore-chip.is-on { border-color: var(--accent); color: var(--text); background: var(--accent-soft); }
button.lore-chip { cursor: pointer; }
button.lore-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ============================================================================
   RULES — two, which is what Cory asked for ("there can be two versions").
   ----------------------------------------------------------------------------
   They mean different things, and that is the whole reason there are two:

     .lore-hr        between two SECTIONS. Different things. Solid.
     .lore-hr-soft   between parts of ONE thing. Dotted, lighter.

   The lead card had four treatments doing this job and a rule above every
   heading, which is what made the column read as a stack of boxes. A rule
   where nothing changes is noise; a heading is usually enough on its own.
   ========================================================================== */
.lore-hr      { border: 0; border-top: 1px solid var(--border);  margin: 12px 0 0; height: 0; }
.lore-hr-soft { border: 0; border-top: 1px dotted var(--border2, var(--border)); margin: 10px 0 0; height: 0; opacity: .8; }

/* The standing lead exits — Put On Hold / Divorce / Mark Lost. They sit in a
   row, do the same KIND of thing, and had three unrelated looks: a hardcoded
   amber pill, a near-black slab and a grey outline, each with its own padding
   and radius. Three choices offered three different ways reads as three
   different systems.
   One modifier: the same button, smaller, because the row they live in is a
   footer rather than the point of the screen. */
.lore-btn.is-sm-exit {
  min-height: 26px;
  padding: 3px 11px;
  border-radius: 12px;
  font-size: 10.5px;
  font-weight: 700;
  background: none;
  color: var(--text3);
}
.lore-btn.is-sm-exit:hover:not(:disabled) { background: var(--bg-hover); color: var(--text); }
@media (pointer: coarse) { .lore-btn.is-sm-exit { min-height: 36px; padding: 8px 14px; } }

/* ── Add-someone form: the name typeahead ────────────────────────────────
 *
 * Measured at 390px, 20 Sep: the match list came out 164px wide — a column
 * too narrow to read a name and an email in, because the name, email and
 * phone inputs were all flexing for room on a phone.
 *
 * On a phone the three fields stack and each gets the full width, so the
 * list under the name does too. The !important is what it takes to beat the
 * inline widths on the inputs themselves; those are inline because the
 * properties panel edits them.
 */
#oi-ts-people-hits { min-width: 220px; }

@media (max-width: 560px) {
  #oi-ts-people-form > div,
  #oi-ts-people-form > input {
    flex: 1 1 100% !important;
    min-width: 100% !important;
  }
}

/* ============================================================================
   SECTIONS AND SUB-SECTIONS — the two levels of in-page navigation.
   ----------------------------------------------------------------------------
   Sage, 1 Oct 2026 (Cory: "segmented accordingly … a simpler, more intuitive,
   elegant design"). A module with a handful of jobs gets ONE row of sections
   (.lore-seg) and, inside the chosen section, one row of sub-sections
   (.lore-subnav) — a website, a social account, a kind of result. Never a
   third row: anything finer is a toggle inside the panel.

     <nav class="lore-seg">  <button class="lore-seg-btn is-on">Home <span class="lore-seg-n">3</span></button> …
     <nav class="lore-subnav"><button class="lore-subnav-btn is-on">outdoors-international.com</button> …

   On a phone the sections share the width equally (icon over word) and the
   sub-sections scroll sideways — the bottom of the phone already belongs to
   the workspace menu, so module navigation lives at the top.
   ========================================================================== */
.lore-seg { display: flex; gap: 2px; padding: 3px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface2); width: max-content; max-width: 100%; }
.lore-seg-btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 34px; padding: 6px 14px; border: 0; border-radius: 9px; background: none; color: var(--text2); font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.lore-seg-btn svg { width: 16px; height: 16px; flex: 0 0 auto; }
.lore-seg-btn:hover { color: var(--text); }
.lore-seg-btn.is-on { background: var(--surface); color: var(--text); box-shadow: 0 0 0 1px var(--border); }
.lore-seg-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.lore-seg-n { min-width: 18px; padding: 0 5px; border-radius: 999px; background: var(--accent-soft); color: var(--text); font-size: 10.5px; font-weight: 800; line-height: 18px; text-align: center; }
.lore-subnav { display: flex; gap: 6px; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; padding: 2px 0; }
.lore-subnav::-webkit-scrollbar { display: none; }
.lore-subnav-btn { display: inline-flex; align-items: center; gap: 6px; min-height: 30px; padding: 5px 12px; border: 1px solid var(--border); border-radius: 999px; background: none; color: var(--text2); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.lore-subnav-btn svg { width: 14px; height: 14px; }
.lore-subnav-btn:hover { color: var(--text); border-color: var(--text3); }
.lore-subnav-btn.is-on { border-color: var(--accent); background: var(--accent-soft); color: var(--text); }
.lore-subnav-btn .lore-seg-n { min-width: 16px; line-height: 16px; font-size: 10px; }
.lore-subnav-btn .q { color: var(--text3); font-weight: 500; }
.lore-subnav-btn.is-add { border-style: dashed; color: var(--text3); }
@media (max-width: 700px) {
  .lore-seg { width: 100%; }
  .lore-seg-btn { flex: 1 1 0; flex-direction: column; gap: 2px; padding: 6px 2px; font-size: 11px; min-height: 46px; position: relative; }
  .lore-seg-btn .lore-seg-n { position: absolute; top: 2px; left: calc(50% + 6px); min-width: 16px; line-height: 15px; font-size: 9.5px; }
}
@media (pointer: coarse) { .lore-subnav-btn { min-height: 36px; } }
