/* obi-app.css: the signed-in app, "borrow lightly" (obi-v2 guides/web.md, "Inside the app (signed in)"; brief b).
   The app stays light (bone and cream), dense and calm. From the stage it borrows only:
     - invisible glass on the controls that answer the pointer: a small light and a lit edge, and ONLY while the mouse is over
       that control (William: the cursor never gives light anywhere else on the signed-in side). obi-app.js places it;
     - the drawn full stop on page titles (obi-app.js draws the span; it drops in when the page changes);
     - the settle spring for cards arriving when the page changes (never on scroll, never while someone types);
     - the clay error band and the busy pattern (Connected folders' "Checking…", the step-up code dialog).
   Interaction movement stays under about 300 ms. The default state is the final state: every movement lives inside
   prefers-reduced-motion: no-preference, and obi-app.js stops the light when Obi's own Motion setting is off. */

/* ============================================================
   1. Invisible glass (the light is a child span obi-app.js puts inside the control while the mouse is over it)
   ============================================================ */
.obi-glass-host { isolation: isolate; }
.obi-glass-host.obi-glass-pos { position: relative; }
.obi-glass { position: absolute; inset: -1px; z-index: -1; pointer-events: none; border-radius: inherit; opacity: 0;
  transition: opacity .18s var(--ease-out, ease-out); }
.obi-glass.is-on { opacity: 1; }
/* on a filled control (the current destination, a primary button) the light is cream, so it shows on the apricot */
:is(.ov-tile.on, .btn:not(.ghost)) > .obi-glass::before { background: radial-gradient(4.5rem circle at var(--gx, 50%) var(--gy, 50%), rgba(255, 253, 248, .42), rgba(255, 253, 248, .12) 55%, transparent 78%); }
:is(.ov-tile.on, .btn:not(.ghost)) > .obi-glass::after { border-color: rgba(255, 253, 248, .85); }
/* the light: warm and small (a 4.5rem pool), between the control's own fill and its words */
.obi-glass::before { content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: radial-gradient(4.5rem circle at var(--gx, 50%) var(--gy, 50%), rgba(232, 133, 58, .16), rgba(232, 133, 58, .05) 55%, transparent 78%); }
/* the edge catching it: an apricot hairline, only near the pointer */
.obi-glass::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 1px solid rgba(201, 102, 30, .55);
  -webkit-mask-image: radial-gradient(4rem circle at var(--gx, 50%) var(--gy, 50%), #000, transparent 75%);
          mask-image: radial-gradient(4rem circle at var(--gx, 50%) var(--gy, 50%), #000, transparent 75%); }
@media (forced-colors: active) { .obi-glass { display: none; } }

/* ============================================================
   2. Page titles: the drawn full stop (a circle, never a typed "."; aria-hidden)
   ============================================================ */
#view h1 .obi-tstop { display: inline-block; width: .2em; height: .2em; margin-left: .06em; border-radius: 50%;
  background: var(--apricot); vertical-align: baseline; transform-origin: 50% 100%; }
@media (forced-colors: active) { #view h1 .obi-tstop { background: CanvasText; forced-color-adjust: none; } }

/* ============================================================
   3. Connected folders (obi-sources.js): the status in words first, a dot that supports it
   ============================================================ */
/* the rows line up as a table: icon, folder, status, actions (the row's inline flex is a free-wrapping line, so the statuses wandered) */
#view .obi-conn ul:has(> .obi-conn-row) { display: grid; grid-template-columns: 20px minmax(0, 1.25fr) minmax(0, 1fr) auto; column-gap: var(--space-4); }
#view .obi-conn .obi-conn-row { display: grid !important; grid-column: 1 / -1; grid-template-columns: subgrid; column-gap: var(--space-4) !important; row-gap: var(--space-2) !important; } /* one set of columns for every row, so a row with one button lines up with the rest */
#view .obi-conn .obi-conn-row > span:last-child { justify-self: end; flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 760px) {
  #view .obi-conn ul:has(> .obi-conn-row) { grid-template-columns: 20px minmax(0, 1fr); }
  #view .obi-conn .obi-conn-row { align-items: start !important; }
  #view .obi-conn .obi-conn-row > :is(.obi-conn-status, span:last-child) { grid-column: 2; justify-self: start; justify-content: flex-start; }
}
#view .obi-conn .obi-conn-status { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--ink-70, var(--muted)); }
#view .obi-conn .obi-conn-status { align-items: flex-start; }
#view .obi-conn .obi-conn-status::before { content: ""; flex: none; width: 8px; height: 8px; margin-top: calc(.75em - 4px)/*on the first line, however many lines*/; border-radius: 50%; background: var(--sage); }
#view .obi-conn .obi-conn-row[data-state="news"] .obi-conn-status { color: var(--apricot-ink); font-weight: var(--fw-semibold); }
#view .obi-conn .obi-conn-row[data-state="news"] .obi-conn-status::before { background: var(--apricot); box-shadow: 0 0 0 3px var(--apricot-tint); }
#view .obi-conn .obi-conn-row[data-state="busy"] .obi-conn-status::before { background: var(--apricot); }
#view .obi-conn .obi-conn-row[data-state="attention"] .obi-conn-status { color: var(--attention-ink); font-weight: var(--fw-semibold); }
#view .obi-conn .obi-conn-row[data-state="attention"] .obi-conn-status::before { background: var(--clay); box-shadow: 0 0 0 3px color-mix(in srgb, var(--clay) 18%, transparent); }
#view .obi-conn .obi-conn-row[data-state="attention"] > span:first-child { color: var(--clay) !important/*the folder icon carries the row's inline muted colour*/; }
#view .obi-conn .obi-conn-row[data-state="idle"] .obi-conn-status::before { background: var(--warm-grey); }
#view .obi-conn .obi-conn-add { padding-top: var(--space-4); border-top: 1px solid var(--panel-line); }
@media (forced-colors: active) { #view .obi-conn .obi-conn-row[data-state] .obi-conn-status::before { background: CanvasText; box-shadow: none; forced-color-adjust: none; } }

/* ============================================================
   4. Admin · Staff (ovation-auth.js viewAdmin): status in words with a dot; the "you" tag; the table on a phone
   ============================================================ */
#view .cred-tbl td { vertical-align: middle; }
#view .cred-tbl select { min-height: var(--control-dense, 36px); }
#view .cred-tbl td[data-obi-status]::before { content: ""; display: inline-block; width: 8px; height: 8px; margin-right: var(--space-2);
  border-radius: 50%; vertical-align: .06em; background: var(--warm-grey); }
#view .cred-tbl td[data-obi-status="ok"]::before { background: var(--sage); }
#view .cred-tbl td[data-obi-status="invited"]::before { background: var(--sky); }
#view .cred-tbl td[data-obi-status="idle"]::before { background: transparent; box-shadow: inset 0 0 0 1.5px var(--warm-grey); }
#view .cred-tbl td:is([data-obi-status="removed"], [data-obi-status=""])::before { visibility: hidden; } /* kept as space, so the words line up */
#view .cred-tbl thead th:nth-child(3)::before { content: ""; display: inline-block; width: 8px; margin-right: var(--space-2); }
#view .cred-tbl tr[data-obi-status="removed"] td, #view .cred-tbl tr[data-obi-status="removed"] td :is(b, .hint) { color: var(--text-muted, #696255); }
#view .cred-tbl tr[data-obi-status="removed"] td b { font-weight: var(--fw-semibold); }
@media (forced-colors: active) { #view .cred-tbl td[data-obi-status]::before { background: CanvasText; forced-color-adjust: none; } }

/* ============================================================
   5. The step-up code dialog (obi-stepup.js: dialog.obi-stepup): Obi's dialog, the sign-in card's code field and clay band
   ============================================================ */
dialog.obi-stepup { border: 1px solid var(--line); border-radius: var(--radius-card); background: var(--surface, var(--cream)); box-shadow: var(--elev-3);
  padding: var(--space-6); color: var(--ink); max-width: min(28rem, calc(100vw - 32px)); }
dialog.obi-stepup::backdrop { background: color-mix(in srgb, var(--ink) 40%, transparent); }
dialog.obi-stepup #obiStepTitle { font-size: var(--text-h3) !important/*inline body-lg: the dialog's one heading*/; line-height: var(--lh-h3, 1.25); letter-spacing: var(--ls-tight, -0.01em); }
dialog.obi-stepup form { grid-template-columns: minmax(0, 1fr); min-width: 0 !important/*inline min(22rem,80vw)*/; }
dialog.obi-stepup #obiStepCode { width: 100%; min-width: 0; box-sizing: border-box; font-size: var(--text-h3); font-weight: var(--fw-semibold); font-variant-numeric: tabular-nums; letter-spacing: .35em;
  padding-left: calc(var(--space-3) + .35em); text-align: center; min-height: 56px; }
@media (max-width: 400px) { dialog.obi-stepup #obiStepCode { letter-spacing: .2em; padding-left: calc(var(--space-3) + .2em); } dialog.obi-stepup { padding: var(--space-5); } }
dialog.obi-stepup #obiStepErr:empty { display: none; }
dialog.obi-stepup #obiStepErr:not(:empty) { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-control); background: var(--concern, #B5573A); color: #FFFFFF; font-weight: var(--fw-semibold); font-size: var(--text-label); }
dialog.obi-stepup #obiStepErr:not(:empty)::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 50%; background: #FFFFFF;
  box-shadow: 0 0 0 3px rgba(255, 255, 255, .28); }

/* ============================================================
   6. Movement (only with no-preference; Obi's Motion setting turns animations off on its own, and obi-app.js stops the light)
   ============================================================ */
@media (prefers-reduced-motion: no-preference) {
  /* cards arriving when the page changes (obi-app.js sets .obi-arriving on #view for one arrival, and --i per card) */
  #view.obi-arriving > :is(.card, section, .grid, .hero) { animation: obiAppSettle 460ms var(--obi-ease-settle, cubic-bezier(.2, .7, .3, 1)) calc(var(--i, 0) * 45ms) both; }
  @keyframes obiAppSettle { from { opacity: .001; translate: 0 10px; } to { opacity: 1; translate: 0 0; } }
  /* the title's full stop drops in with the page, once */
  #view.obi-arriving h1 .obi-tstop { animation: obiAppStop 620ms var(--obi-ease-pop, cubic-bezier(.3, 1.4, .5, 1)) 140ms both; }
  @keyframes obiAppStop { 0% { opacity: 0; transform: translateY(-.55em) scale(.6); } 60% { opacity: 1; transform: translateY(.04em) scaleY(.82) scaleX(1.12); } 100% { transform: none; } }
  /* the busy pattern: the dot hops while a folder is being checked */
  #view .obi-conn .obi-conn-row[data-state="busy"] .obi-conn-status::before { animation: obiAppHop 700ms cubic-bezier(.3, .7, .4, 1) infinite; }
  @keyframes obiAppHop { 0%, 100% { transform: translateY(0); } 45% { transform: translateY(-4px); } }
  /* toasts arrive on the settle curve */
  .toast.ov-t { transition-timing-function: var(--obi-ease-settle, cubic-bezier(.2, .7, .3, 1)); }
  dialog.obi-stepup[open] { animation: obiAppSettle 300ms var(--obi-ease-settle, cubic-bezier(.2, .7, .3, 1)) both; }
}

/* ============================================================
   7. Narrow screens: the header scrolls with the page (found in this pass, at 375 wide)
   At 860px and below the rail is a row above the page, and the header wraps to two or three rows (145px at 375 wide).
   Sticky, it covered the rail whenever a page change scrolled to the content, and kept almost a fifth of a phone's height.
   It now scrolls away like the rail does; Search, messages and Help are one scroll up. Wide screens are unchanged.
   ============================================================ */
@media (max-width: 860px) { body.ov-shelled > header { position: relative; } html:has(> body.ov-shelled) { scroll-padding-top: 0 !important; } }
