/* site.css */
/* otto365.ai design system.
   Brand tokens verified live (apps365ltd.com / apps365.ai, 2026-09-22): indigo #4554EA, AA indigo text #3A46C8,
   ink #0F162E, charcoal logo #232323. Fonts match the Brief videos (Poppins) + Inter for reading.
   Readability rules (docs/research/20): 19px article body, 50-75 characters per line, line height >= 1.5. */

@font-face { font-family: "Poppins"; font-weight: 600; font-display: swap; src: url("../fonts/poppins-latin-600.woff2") format("woff2"); }
@font-face { font-family: "Poppins"; font-weight: 700; font-display: swap; src: url("../fonts/poppins-latin-700.woff2") format("woff2"); }
@font-face { font-family: "Poppins"; font-weight: 800; font-display: swap; src: url("../fonts/poppins-latin-800.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-weight: 100 900; font-display: swap; src: url("../fonts/inter-latin-var.woff2") format("woff2"); }
/* Arial sized to Inter's metrics (measured in Chrome), so the swap to Inter moves no text (CLS; review round 2,
   R2-19). Systems without Arial skip it and use the next font in the stack. */
@font-face { font-family: "Inter Fallback"; src: local("Arial"); size-adjust: 106.81%; ascent-override: 90.81%; descent-override: 22.47%; line-gap-override: 0%; }

:root {
  --indigo: #4554EA;
  --indigo-text: #3A46C8;
  --violet: #6E63F0;
  --lavender: #948CF6;
  --wash: #E8E8F6;
  --ink: #0F162E;
  --ink-2: #3E445B;
  --ink-3: #5B6275;
  --paper: #F6F7FB;
  --card: #FFFFFF;
  --line: #E3E5EE;
  --night: #0A0F24;
  --night-2: #131A38;
  --on-night: #EEF0FC;
  --on-night-2: #B4B9D6;
  --ok: #0E7C4A;
  --warn: #9A5B00;

  --font-display: "Poppins", "Segoe UI", system-ui, sans-serif;
  --font-text: "Inter", "Inter Fallback", "Segoe UI", system-ui, sans-serif;

  --radius: 18px;
  --radius-sm: 12px;
  --shadow: 0 1px 2px rgba(15, 22, 46, .06), 0 8px 28px rgba(15, 22, 46, .08);
  --wrap: 1180px;
  --measure: 68ch;
  --gutter: clamp(18px, 4vw, 40px);
  --header-h: 68px;
  color-scheme: light;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; scroll-padding-top: calc(var(--header-h) + 12px); }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 400 17px/1.6 var(--font-text);
  font-feature-settings: "cv11", "ss01";
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, svg, video, canvas { display: block; max-width: 100%; }
img, video { height: auto; }
a { color: var(--indigo-text); text-underline-offset: .18em; text-decoration-thickness: .08em; }
a:hover { color: var(--ink); }
/* Focus ring: indigo on light surfaces; dark surfaces set --focus to a light lavender so the ring keeps
   3:1 against what is behind it (WCAG 1.4.11). The ring sits outside the element, so --focus is set on the
   dark container, never on a focusable that itself sits on a light background. */
:focus-visible { outline: 3px solid var(--focus, var(--indigo)); outline-offset: 3px; border-radius: 6px; }
.on-night, .promo-inline, .promo-card, .sub-promo, .site-footer, .site-header.on-dark, .stop[data-theme="dark"] { --focus: #C9C5FF; }
h1, h2, h3, h4 { font-family: var(--font-display); color: var(--ink); line-height: 1.15; letter-spacing: -.015em; margin: 0 0 .5em; text-wrap: balance; }
h1 { font-size: clamp(2.1rem, 1.3rem + 3.4vw, 3.9rem); font-weight: 800; }
h2 { font-size: clamp(1.6rem, 1.2rem + 1.7vw, 2.5rem); font-weight: 800; }
h3 { font-size: clamp(1.15rem, 1.05rem + .4vw, 1.35rem); font-weight: 700; letter-spacing: -.005em; }
p { margin: 0 0 1em; text-wrap: pretty; }
.ic { display: inline-block; vertical-align: -0.18em; width: 1.15em; height: 1.15em; flex: none; }
.sr-only { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.skip { position: absolute; left: 12px; top: -60px; z-index: 100; background: var(--ink); color: #fff; padding: 10px 16px; border-radius: 10px; font-weight: 600; }
.skip:focus { top: 12px; color: #fff; }

.wrap { width: min(100% - 2 * var(--gutter), var(--wrap)); margin-inline: auto; }
.measure { max-width: var(--measure); }
/* The icon or the On Air dot sits on the FIRST line when an eyebrow wraps, not between its lines (review round 3,
   d3r3-14: align-items was center). Both are centred on that line's 1.2em line box, so a one-line eyebrow looks as
   before: the 1.15em icon moves .005em (under 0.1 px), the 9px dot not at all. */
.eyebrow { font: 600 .8rem/1.2 var(--font-text); letter-spacing: .14em; text-transform: uppercase; color: var(--indigo-text); display: inline-flex; align-items: flex-start; gap: .5em; margin-bottom: 1rem; }
.eyebrow > .ic { margin-top: .02em; }
.eyebrow .dot { flex: none; margin-top: calc(.6em - 4.5px); }
.lead { font-size: clamp(1.1rem, 1rem + .5vw, 1.3rem); color: var(--ink-2); line-height: 1.55; }
.muted { color: var(--ink-3); }
.num { font-variant-numeric: tabular-nums; }

/* Buttons: pill, one primary per view (TikTok lesson: one fixed CTA). */
.btn { --b: var(--indigo); display: inline-flex; align-items: center; gap: .55em; min-height: 48px; padding: 0 22px; border-radius: 999px; border: 1.5px solid var(--b); background: var(--b); color: #fff; font: 600 1rem/1 var(--font-text); text-decoration: none; cursor: pointer; transition: transform .15s ease, box-shadow .2s ease, background .2s ease; }
.btn:hover { color: #fff; background: #3743d6; box-shadow: 0 8px 24px rgba(69, 84, 234, .32); transform: translateY(-1px); }
.btn.ghost { background: transparent; color: var(--ink); border-color: rgba(15, 22, 46, .22); }
.btn.ghost:hover { background: #fff; color: var(--ink); box-shadow: var(--shadow); }
.btn.sm { min-height: 40px; padding: 0 16px; font-size: .92rem; }
.on-night .btn.ghost { color: var(--on-night); border-color: rgba(238, 240, 252, .35); }
.on-night .btn.ghost:hover { background: rgba(255, 255, 255, .08); color: #fff; }

/* Header: fixed logo + one CTA, always. The home header gets .solid and .on-dark from script; without script it
   stays the solid paper header, so it reads over the night stops too (review round 2, R2-15). */
.site-header { position: fixed; inset: 0 0 auto 0; z-index: 50; height: var(--header-h); display: flex; align-items: center; transition: background .25s ease, box-shadow .25s ease, backdrop-filter .25s ease; }
.site-header.solid, .page .site-header, .home .site-header { background: rgba(246, 247, 251, .86); backdrop-filter: saturate(1.4) blur(12px); -webkit-backdrop-filter: saturate(1.4) blur(12px); box-shadow: 0 1px 0 var(--line); }
.site-header .wrap { display: flex; align-items: center; gap: 24px; }
.brand { display: inline-flex; align-items: center; flex: none; }
.brand img { height: 36px; width: auto; }
.brand .logo-white { display: none; }
.site-header.on-dark .brand .logo-white { display: block; }
.site-header.on-dark .brand .logo-ink { display: none; }
/* .82 keeps every nav link at 5.5:1 or more over the brightest tunnel frame (review round 2, R2-22; .74 gave 4.68:1). */
.site-header.on-dark.solid { background: rgba(10, 15, 36, .82); box-shadow: 0 1px 0 rgba(255, 255, 255, .07); }
.nav { display: flex; gap: 4px; margin-left: auto; }
.nav a { color: var(--ink-2); text-decoration: none; font-weight: 550; font-size: .95rem; padding: 8px 12px; border-radius: 999px; white-space: nowrap; }
/* aria-current="page" on the page itself, "true" on the section a page sits in (an episode under The Brief). */
.nav a:hover, .nav a[aria-current="page"], .nav a[aria-current="true"] { color: var(--ink); background: rgba(15, 22, 46, .06); }
.site-header.on-dark .nav a { color: var(--on-night-2); }
.site-header.on-dark .nav a:hover { color: #fff; background: rgba(255, 255, 255, .08); }
.menu-btn { display: none; margin-left: auto; background: none; border: 0; padding: 10px; color: inherit; cursor: pointer; }
/* While the menu is open the button shows a close cross (review round 3, d3r3-22); its name stays "Menu" and
   aria-expanded says whether it is open. */
.menu-btn .ic-close { display: none; }
.menu-btn[aria-expanded="true"] .ic-close { display: block; }
.menu-btn[aria-expanded="true"] .ic:not(.ic-close) { display: none; }
.site-header .btn.sm { flex: none; white-space: nowrap; } /* the one CTA is never squeezed or wrapped */
/* While its own target (the sign-up form) is on screen, the header CTA steps back to a ghost pill (layout.php,
   review round 2, R2-24), so one filled "Get the Brief" button is in view. On the dark header it stays readable. */
.site-header.on-dark .btn.sm.ghost { color: var(--on-night); border-color: rgba(238, 240, 252, .35); }
.site-header.on-dark .btn.sm.ghost:hover { background: rgba(255, 255, 255, .08); color: #fff; }
/* Up to 1100px the links sit behind the Menu button (review round 2, R2-23): with text spacing (WCAG 1.4.12) the
   full header no longer fits there and the CTA was cut off. */
@media (max-width: 1100px) {
  /* The open menu scrolls on its own when the screen is short (320 x 256 at 400% zoom, WCAG 1.4.10). It is a
     paper panel even under a dark header, so its focus ring is indigo. */
  .nav { display: none; position: absolute; top: var(--header-h); left: 0; right: 0; flex-direction: column; background: var(--paper); padding: 10px var(--gutter) 18px; box-shadow: 0 12px 24px rgba(15, 22, 46, .12); max-height: calc(100vh - var(--header-h)); max-height: calc(100dvh - var(--header-h)); overflow-y: auto; overscroll-behavior: contain; --focus: var(--indigo); }
  .nav.open { display: flex; }
  .nav a { padding: 12px; font-size: 1.05rem; color: var(--ink) !important; }
  .menu-btn { display: inline-flex; margin-left: 0; }
  .site-header.on-dark .menu-btn { color: #fff; }
  .site-header .btn.sm { margin-left: auto; min-height: 38px; padding: 0 14px; font-size: .88rem; }
  .brand img { height: 32px; }
  .site-header .wrap { gap: 8px; }
}

main { display: block; }
.page main { padding-top: calc(var(--header-h) + clamp(24px, 5vw, 56px)); padding-bottom: clamp(48px, 8vw, 96px); }
.section { padding-block: clamp(56px, 9vw, 112px); }
.card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); }
.chip { display: inline-flex; align-items: center; gap: .4em; padding: 5px 12px; border-radius: 999px; font: 600 .82rem/1.2 var(--font-text); background: var(--wash); color: var(--indigo-text); }
.chip.ai { background: #EEF0FF; color: var(--ink-2); }
.breadcrumbs { font-size: .9rem; color: var(--ink-3); margin-bottom: 18px; }
.breadcrumbs a { color: var(--ink-3); }

/* Long-form reading (policy pages + episode text). */
.prose { max-width: var(--measure); font-size: 1.1875rem; line-height: 1.65; color: var(--ink-2); overflow-wrap: anywhere; }
.prose h2 { font-size: clamp(1.35rem, 1.2rem + .7vw, 1.75rem); margin-top: 2em; color: var(--ink); }
.prose h3 { margin-top: 1.6em; }
.prose ul, .prose ol { padding-left: 1.2em; }
.prose li { margin: .35em 0; }
.prose strong { color: var(--ink); }
.updated { font-size: .9rem; color: var(--ink-3); }

/* Footer */
.site-footer { position: relative; z-index: 1; background: var(--night); color: var(--on-night-2); padding: 64px 0 36px; font-size: .95rem; }
.site-footer a { color: var(--on-night); text-decoration: none; }
.site-footer a:hover { text-decoration: underline; color: #fff; }
.foot-grid { display: grid; grid-template-columns: 1.4fr 1fr 1fr 1.1fr 1fr; gap: 32px; }
.foot-grid h2 { font: 700 .82rem/1.2 var(--font-text); letter-spacing: .12em; text-transform: uppercase; color: var(--on-night-2); margin-bottom: 14px; }
.foot-grid ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 9px; }
.foot-brand img { height: 40px; width: auto; margin-bottom: 16px; }
.foot-brand p { max-width: 34ch; }
.disclosure-line { display: inline-flex; gap: .5em; align-items: center; color: var(--on-night); font-weight: 600; }
.foot-legal { border-top: 1px solid rgba(255, 255, 255, .1); margin-top: 44px; padding-top: 22px; display: flex; flex-wrap: wrap; gap: 8px 22px; justify-content: space-between; font-size: .86rem; }
/* Links inside running text are underlined, not told apart by colour alone (WCAG 1.4.1). */
.foot-legal a { text-decoration: underline; text-underline-offset: .18em; }
.social { display: flex; gap: 8px; flex-wrap: wrap; }
.social a { display: inline-flex; width: 40px; height: 40px; align-items: center; justify-content: center; border-radius: 12px; background: rgba(255, 255, 255, .06); }
.social a:hover { background: rgba(255, 255, 255, .14); }
.social svg { width: 18px; height: 18px; }
@media (max-width: 900px) { .foot-grid { grid-template-columns: 1fr 1fr; } .foot-brand { grid-column: 1 / -1; } }
@media (max-width: 520px) { .foot-grid { grid-template-columns: 1fr; } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
}
.foot-brand .commercial { font-size: .88rem; max-width: 36ch; }

/* Content pages */
.page-head { margin-bottom: clamp(28px, 4vw, 44px); }
.page-head .lead { margin-top: .4em; }
/* Inner pages carry a quiet echo of the home page's On Air world: the tunnel of glyph tiles with the Apps 365
   glyph (three tiles and a plus) at its heart. CSS background only, so it is never read out, and it never
   sits under text: a small tile beside the breadcrumb row, or beside the page head on wide screens. It is a
   float (not absolutely positioned), so contrast checkers can still read the page background; the negative
   bottom margin keeps it from pushing any line below the breadcrumbs. */
.wrap:has(> .page-head)::before {
  content: ""; float: right; position: relative; z-index: -1; pointer-events: none;
  width: 44px; height: 44px; margin: -10px 0 -14px 12px; opacity: .9;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' fill='none'%3E%3Crect x='2' y='2' width='236' height='236' rx='37' stroke='%234554EA' stroke-opacity='.14' stroke-width='1.5'/%3E%3Crect x='26' y='26' width='188' height='188' rx='29.5' stroke='%236E63F0' stroke-opacity='.2' stroke-width='1.75'/%3E%3Crect x='47' y='47' width='146' height='146' rx='23' stroke='%23948CF6' stroke-opacity='.3' stroke-width='2'/%3E%3Crect x='65' y='65' width='110' height='110' rx='17' stroke='%234554EA' stroke-opacity='.36' stroke-width='2.25'/%3E%3Crect x='80' y='80' width='80' height='80' rx='12.5' stroke='%236E63F0' stroke-opacity='.5' stroke-width='2.5'/%3E%3Cg fill='%234554EA' fill-opacity='.5'%3E%3Crect x='99' y='99' width='18' height='18' rx='4.5'/%3E%3Crect x='123' y='99' width='18' height='18' rx='4.5'/%3E%3Crect x='99' y='123' width='18' height='18' rx='4.5'/%3E%3Crect x='124' y='130' width='16' height='4' rx='2'/%3E%3Crect x='130' y='124' width='4' height='16' rx='2'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat,
    radial-gradient(closest-side, rgba(148, 140, 246, .16), rgba(148, 140, 246, 0));
}
@media (min-width: 1100px) {
  .wrap:has(> .page-head)::before { width: clamp(240px, 22vw, 300px); height: clamp(240px, 22vw, 300px); margin: 6px 0 -100% 24px; opacity: .8; }
}
/* A page with its own picture (prose.php $figure, review round 2, R2-53: /about shows Otto365 at the desk) floats
   it beside the page head instead of the tunnel tile. Below 1100px the small tile is not shown (it read as a stray
   icon there), and the picture sits centred above the page head at up to 300px (review round 3, d3r3-18: before,
   phones and tablets never saw Otto365 on /about). */
.page-figure { float: right; width: clamp(240px, 24vw, 320px); margin: 0 0 16px 32px; }
.page-figure img { width: 100%; height: auto; }
.wrap:has(> .page-figure)::before { content: none; }
@media (max-width: 1099px) {
  .page-figure { display: block; float: none; width: min(70vw, 300px); margin: 0 auto 8px; }
  .wrap:has(> .page-head)::before { content: none; }
}
/* A row of process steps on a content page (R2-53, /how-we-make-the-brief): four across from 700px, two below. */
.step-row, .prose .step-row { list-style: none; margin: 0 0 clamp(28px, 4vw, 40px); padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.step-row li, .prose .step-row li { margin: 0; display: grid; gap: 8px; align-content: start; padding: 14px 16px; background: #fff; border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--ink); font: 650 1rem/1.35 var(--font-text); }
.step-row .ic { width: 26px; height: 26px; color: var(--indigo-text); }
@media (min-width: 700px) { .step-row, .prose .step-row { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.callout { display: flex; gap: 12px; align-items: flex-start; background: var(--wash); border-radius: var(--radius-sm); padding: 16px 18px; color: var(--ink); }
.callout .ic { color: var(--indigo-text); margin-top: .25em; }
.prose .empty { display: flex; gap: 10px; align-items: flex-start; background: #fff; border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 14px 16px; }
.prose .empty .ic { color: var(--ok); margin-top: .3em; }
.prose code { font-size: .9em; background: var(--wash); padding: .1em .35em; border-radius: 6px; }
.log { list-style: none; padding: 0; display: grid; gap: 14px; }
.prose .log { padding-left: 0; }
.log li { background: #fff; border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 14px 16px; }
.empty-state { display: flex; gap: clamp(18px, 4vw, 36px); align-items: flex-start; padding: clamp(24px, 5vw, 48px); max-width: 860px; }
.empty-state .actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 22px; }
.empty-mark { flex: none; width: clamp(72px, 12vw, 120px); aspect-ratio: 1; border-radius: 28px; background: var(--wash); display: grid; place-items: center; }
.empty-mark img { width: 72%; }
@media (max-width: 600px) { .empty-state { flex-direction: column; align-items: flex-start; } }
.ep-grid { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr)); gap: 18px; }
.ep-card { position: relative; display: grid; grid-template-columns: 92px 1fr; gap: 16px; padding: 14px; align-items: start; }
.ep-card:hover { box-shadow: 0 1px 2px rgba(15, 22, 46, .06), 0 14px 36px rgba(15, 22, 46, .14); }
.ep-thumb { width: 92px; aspect-ratio: 9 / 16; object-fit: cover; border-radius: 12px; background: var(--night); }
.ep-text { display: grid; gap: 6px; }
.ep-meta { display: flex; gap: 8px; align-items: center; margin: 0; font-weight: 700; color: var(--indigo-text); }
.ep-title { font: 700 1.15rem/1.3 var(--font-display); margin: 0; }
.ep-title a { color: var(--ink); text-decoration: none; }
.ep-title a:hover { text-decoration: underline; }
/* The whole card is clickable, but the link text stays the headline. */
.ep-title a.stretched::after { content: ""; position: absolute; inset: 0; border-radius: var(--radius); }
.ep-title a.stretched:focus-visible::after { outline: 3px solid var(--focus, var(--indigo)); outline-offset: 3px; }
.ep-hook { color: var(--ink-2); margin: 0; }
.chip.warn { background: #FFF3E0; color: var(--warn); }
.nav a.nav-out { display: inline-flex; align-items: center; gap: 4px; color: var(--indigo-text); font-weight: 650; }
.site-header.on-dark .nav a.nav-out { color: #C9C5FF; }
.nav a.nav-out .ic { width: .95em; height: .95em; }

/* Google's preferred-source link sits under the follow pills as a text link (subscribe-follow.php, review round 3,
   d3r3-23): as a pill its long label wrapped onto two lines on phones. */
.sub-follow-more { margin: 14px 0 0; font-size: .95rem; }
.sub-follow-more .ic { width: 1em; height: 1em; margin-left: .15em; }

/* Windows contrast themes paint the header in the theme's own colours, and the logo (a picture, never recoloured)
   vanished on it: the charcoal lockup on a black theme (review round 3, a11y-r3-9). It keeps a white plate, so the
   charcoal lockup is the one shown there, also on the dark home header (the white lockup would vanish on the plate). */
@media (forced-colors: active) {
  .brand img { forced-color-adjust: none; background: #fff; border-radius: 8px; padding: 2px 6px; }
  .site-header.on-dark .brand .logo-white { display: none; }
  .site-header.on-dark .brand .logo-ink { display: block; }
}

/* Without script (review round 2, R2-15) the Menu button could not open the menu and the Pause button has nothing to
   pause, so neither is shown; every header link is also in the footer. !important beats home.css's reserved-space
   rule for the Pause button (.motion-btn.pending[hidden], 0,3,0). Where the links sit behind the Menu button, the
   CTA keeps its place at the right: home.css takes its auto margin away when the Pause button comes before it. */
@media (scripting: none) {
  .menu-btn, .motion-btn { display: none !important; }
}
@media (scripting: none) and (max-width: 1100px) {
  .site-header .btn.sm { margin-left: auto !important; }
}
/* home.css */
/* Home: one fixed canvas (decoration, aria-hidden) behind native-scrolling HTML stops.
   Without WebGL / with reduced motion / before the 3D loads, each stop paints its own background
   and the hero shows a still render of the 3D Otto365 (the poster). */

.home { background: var(--night); }
.stage { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.stage canvas { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; transition: opacity .9s ease; }
.stage.ready canvas { opacity: 1; }
.stage.parked { visibility: hidden; }
/* The hero poster (the LCP image, and the picture without 3D or on Pause) is also where the live studio sits: home3d.js
   fits the studio into the poster's studio area (the middle 1/1.3 of the square, lifted 3.5%), so the two cross-fade in
   place at every screen size (review round 3). Beside the panel: centred in the room between the panel and the wrap's
   edge (--room), level with the panel's centre (the screen's centre when the hero is taller than the screen); the
   studio takes 95% of the room's width, or the screen's free height on short laptop screens, whichever is less (as the
   live studio, which stays within its free zone). The transparent margin of the square may pass the screen's edge:
   the hero clips it (no sideways scroll; "hidden" first for Safari 15, which has no "clip"). */
.stop-hero { --gap: max(20px, 2vw); --panel-w: min(42rem, 58vw); --room: calc(min(100% - 2 * var(--gutter), var(--wrap)) - var(--panel-w) - var(--gap)); overflow-x: hidden; overflow-x: clip; }
.stop-hero picture { display: contents; } /* no box of its own: the poster is laid out as the section's own child */
.stop-hero .poster { position: absolute; z-index: 0; left: calc(50% + (var(--panel-w) + var(--gap)) / 2); top: calc(min(50%, 50svh) + 22px); width: min(calc(1.24 * var(--room)), calc(1.22 * (100svh - var(--header-h) - max(14px, 3vh) - max(18px, 5vh)))); height: auto; transform: translate(-50%, -46.5%); transition: opacity .6s ease; }
.stop-hero .wrap { position: relative; z-index: 1; }
.has-3d .stop-hero .poster { opacity: 0; }

.stop { position: relative; z-index: 1; min-height: 100vh; min-height: 100svh; display: flex; align-items: center; padding: calc(var(--header-h) + 24px) 0 48px; }
.stop .wrap { display: flex; align-items: center; gap: clamp(24px, 4vw, 64px); }
.stop.right .wrap { justify-content: flex-end; }
.panel { max-width: 34rem; }
/* The panel box is the same with and without 3D, so nothing moves when the canvas arrives (CLS 0). */
.stop .panel { padding: clamp(20px, 3vw, 32px); border-radius: 22px; }
.stop-hero .panel { max-width: 42rem; }
.stop-hero h1 { font-size: clamp(1.95rem, 1.2rem + 3vw, 3.5rem); } /* 3 lines from 360px to 1920px */
.stop h2 { font-size: clamp(1.7rem, 1.2rem + 1.9vw, 2.7rem); }
.stop p { font-size: 1.125rem; }
/* .stop p (0,1,1) would set every eyebrow and the promotion label at body size (R2-46): 0,2,0 puts them back. */
.stop .eyebrow { font-size: .875rem; }
.stop .promo-label { font-size: .75rem; }
.actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; margin: 26px 0 0; }
.eyebrow .dot { width: 9px; height: 9px; border-radius: 50%; background: #E5484D; box-shadow: 0 0 0 4px rgba(229, 72, 77, .18); }
/* Pulses twice and stops (ends within 5 s, WCAG 2.2.2), so it needs no pause control. */
@media (prefers-reduced-motion: no-preference) { .eyebrow .dot { animation: onair 1.8s ease-in-out 2; } }
@keyframes onair { 50% { box-shadow: 0 0 0 8px rgba(229, 72, 77, 0); } }

/* Stills of each stop's 3D subject (review item 32): only while the 3D layer is not running (reduced motion, or
   home.js found no WebGL / a low-memory device and set .no-3d). Otherwise display: none, so the lazy images are
   never fetched under the canvas. Beside the panel on wide screens, above it on phones and tablets. */
.stop .still { display: none; flex: 0 1 auto; width: min(42%, 520px); height: auto; max-height: 70vh; object-fit: contain; margin-left: auto; }
.stop.right .still { order: -1; margin: 0 auto 0 0; }
/* Live, stop 2's still is the episode's 16:9 poster, a full-bleed frame: framed as a card like the 3D one (d3r3-10). */
.stop .still-poster { border-radius: 18px; box-shadow: 0 0 0 1px rgba(148, 140, 246, .3), 0 24px 60px rgba(0, 0, 0, .4); }
.no-3d .stop .still { display: block; }
@media (prefers-reduced-motion: reduce) {
  .stop .still { display: block; }
  /* No camera rail to pace: each stop is as tall as its content. */
  .stop:not(.stop-hero) { min-height: auto; padding: 72px 0; }
}
/* JavaScript off: nothing sets .no-3d, so the stills show from CSS alone (R2-16; mirrors .no-3d below). */
@media (scripting: none) { .stop .still { display: block; } }
@media (scripting: none) and (max-width: 860px) { .stop .wrap { flex-direction: column; align-items: center; align-items: safe center; } }

/* Stop backgrounds: used as-is when there is no canvas; transparent over the canvas. */
.stop-hero { background: radial-gradient(120% 90% at 75% 45%, #FFFFFF 0%, #EEF0FA 55%, #E4E7F5 100%); }
.stop[data-theme="dark"] { background: radial-gradient(110% 80% at 60% 50%, #1A2250 0%, var(--night) 70%); }
.has-3d .stop { background: transparent; }
/* Pause motion stops ALL motion (rule R14, R2-08): the canvas goes, and each stop shows its still. Play lands straight
   on the stop in view. The swap is one step, with nothing in between (d3r3-4): the poster shows at once (its 0.6 s fade
   in left the hero empty), and every stop keeps the canvas's own backdrop, the studio's #EEF0FA and the night (the
   radial glows of the flat page lit up the whole stop the moment the canvas went; the flat page without 3D keeps
   them). home.js swaps in the stills once they are decoded. */
.motion-paused .stage { visibility: hidden; }
.has-3d.motion-paused .stop-hero { background: #EEF0FA; }
.has-3d.motion-paused .stop[data-theme="dark"] { background: var(--night); }
.has-3d.motion-paused .stop-hero .poster { opacity: 1; transition: none; }
.motion-paused .stop .still { display: block; }
@media (max-width: 860px) { .motion-paused .stop .wrap { flex-direction: column; align-items: center; align-items: safe center; } }
/* A still that appears or goes (Pause, Play) moves the top of its wrap, and scroll anchoring then scrolled the page
   with it: on phones and tablets the words being read jumped 200 to 460px, off the screen on a tablet. The stop
   (the section) stays the anchor, so the panel keeps its place. */
.stop .wrap { overflow-anchor: none; }

/* Text panels over 3D keep AA contrast against the brightest frame (rule R6): a soft scrim. The hero's right half
   stays at .85 because the dark studio screen passes behind it during the dolly: over the darkest scene colour
   (#0A0F24) that is #D3D4DB, so the lead keeps 6.5:1 even without the blur (review round 2, R2-05). */
.has-3d .stop-hero .panel { background: linear-gradient(90deg, rgba(246, 247, 251, .92), rgba(246, 247, 251, .85)); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.has-3d .panel.on-night { background: rgba(10, 15, 36, .8); backdrop-filter: blur(10px) saturate(1.2); -webkit-backdrop-filter: blur(10px) saturate(1.2); box-shadow: inset 0 0 0 1px rgba(148, 140, 246, .18); }
.on-night, .on-night h2, .on-night h3 { color: var(--on-night); }
.on-night p, .on-night li { color: var(--on-night-2); }
.on-night .eyebrow { color: #C9C5FF; }
.on-night strong { color: #fff; }
.on-night .promo-label { color: #fff; } /* .on-night p must not dim the label: white keeps AA over the brightest frame */
.muted-night { color: var(--on-night-2); font-size: .95rem !important; }
.link-night { color: #C9C5FF; }
.link-night:hover { color: #fff; }

.today { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin-top: 26px; padding: 12px 16px; border-radius: 16px; background: rgba(255, 255, 255, .7); border: 1px solid var(--line); color: var(--ink); max-width: 36rem; }
.stop .today { font-size: 1rem; }
/* Only the headline is a link. The code chip and the date share the first row and the headline takes a row of its
   own, at every width (it wrapped differently by width: d3r3-21). */
.today-title { flex: 1 0 100%; font-weight: 650; color: var(--ink); }
.today-title:hover { color: var(--indigo-text); }
.today.pending { color: var(--ink-2); }

.points { margin: 18px 0 0; padding-left: 1.3em; display: grid; gap: 12px; }
.points strong { display: block; }
.proof { list-style: none; margin: 18px 0 22px; padding: 0; display: grid; gap: 12px; }
.proof li { display: flex; gap: 12px; align-items: flex-start; }
.proof .ic { color: var(--lavender); margin-top: .2em; }
.ep-list { list-style: none; margin: 16px 0 22px; padding: 0; display: grid; gap: 8px; }
/* One row per episode: the code, the headline (the only link) and the date (review item 17). */
.ep-list li { display: flex; flex-wrap: wrap; gap: 4px 12px; align-items: baseline; padding: 10px 12px; border-radius: 12px; background: rgba(255, 255, 255, .05); }
.ep-list .num { color: var(--on-night-2); }
.ep-list a { color: var(--on-night); font-weight: 600; text-underline-offset: .18em; }
.ep-list a:hover { color: #fff; }
.ep-list time { margin-left: auto; color: var(--on-night-2); font-size: .9rem; }

/* Flat conversion content: the canvas is paused behind it (rule R10). */
.flat { position: relative; z-index: 1; background: var(--paper); }
.get-grid { display: grid; grid-template-columns: 1.1fr 1fr; gap: 40px; align-items: center; }
.follow { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px; }
.promo-label { display: inline-block; font: 700 .75rem/1 var(--font-text); letter-spacing: .14em; text-transform: uppercase; color: #fff; background: rgba(255, 255, 255, .12); padding: 7px 12px; border-radius: 999px; }

/* Pause motion (WCAG 2.2.2): shown only once motion is running. */
.motion-btn { position: relative; display: inline-flex; align-items: center; gap: 6px; background: transparent; border: 1px solid rgba(15, 22, 46, .18); color: var(--ink-2); border-radius: 999px; min-height: 40px; padding: 0 14px; font: 600 .88rem/1 var(--font-text); cursor: pointer; }
.motion-btn[hidden] { display: none; } /* the hidden attribute must win: no 3D, no button */
/* While the 3D layer may still start, the button keeps its place (invisible, not focusable, not read out), so
   the header does not move when it appears (CLS). home.js drops .pending as soon as it knows either way. */
@media (prefers-reduced-motion: no-preference) { .motion-btn.pending[hidden] { display: inline-flex; visibility: hidden; } }
.motion-btn .when-off { display: none; }
.motion-btn[data-paused="1"] .when-on { display: none; }
.motion-btn[data-paused="1"] .when-off { display: inline-flex; }
.motion-btn span { display: inline-flex; align-items: center; gap: 6px; }
.site-header.on-dark .motion-btn { color: var(--on-night-2); border-color: rgba(255, 255, 255, .25); }

@media (max-width: 860px) {
  .stop { align-items: flex-end; padding-bottom: 28px; }
  .stop.right .wrap { justify-content: flex-start; }
  /* Portrait: the poster (and so the live studio) sits in a band the hero keeps free above its panel (--band, at least
     34vh), clear of the eyebrow wherever it shows: JavaScript off, reduced motion, no WebGL, while the 3D loads, and on
     Pause. The studio fills the band's height (less the header's margin and a 12px gap), centred on the wrap. */
  .stop-hero { --band: 34vh; align-items: flex-end; padding-top: calc(var(--header-h) + var(--band)); }
  .stop-hero .poster { left: 50%; top: calc(var(--header-h) + (max(14px, 3vh) + var(--band) - 12px) / 2); width: min(1.222 * (var(--band) - 12px - max(14px, 3vh)), 1.24 * (100% - 2 * var(--gutter))); }
  /* Phones have no backdrop blur (below), so a see-through scrim showed the studio as a sharp double exposure
     behind the text on the first scroll (R2-01). The hero panel is solid paper; the dark panels are nearly
     solid night, so their own subject no longer ghosts through the words. */
  .has-3d .stop-hero .panel { background: var(--paper); }
  .has-3d .panel.on-night { background: rgba(10, 15, 36, .94); }
  .get-grid { grid-template-columns: 1fr; }
}
/* Tablets: the panel sits at the foot of the hero and leaves more than 34vh above it. The band is what a panel of P
   leaves (P: a little more than the panel's measured height at these widths, 451 to 539px), so the panel stays where
   it was, and a taller one (larger text, zoom) grows the section instead of meeting the studio. */
@media (min-width: 600px) and (max-width: 699px) { .stop-hero { --band: max(34vh, 100svh - 96px - 555px); } }
@media (min-width: 700px) and (max-width: 839px) { .stop-hero { --band: max(34vh, 100svh - 96px - 490px); } }
@media (min-width: 840px) and (max-width: 860px) { .stop-hero { --band: max(34vh, 100svh - 96px - 545px); } }
/* 861 to 1179px: the 42rem panel narrows (58vw), with and without 3D, so nothing moves when the canvas arrives
   (CLS 0); the poster's --room follows it. */
@media (min-width: 861px) and (max-width: 1179px) {
  .stop-hero .panel { max-width: min(42rem, 58vw); }
}
/* Up to 1100px the nav sits behind the Menu button (site.css, R2-23), so the header is logo, Pause, CTA, Menu: the
   Pause button (shown or reserved) takes the free space; without it the CTA keeps its own auto margin, so the CTA
   never sits beside the logo. */
@media (max-width: 1100px) {
  .motion-btn { margin-left: auto; }
  .motion-btn:not([hidden]) + .btn.sm { margin-left: 0; }
}
@media (max-width: 860px) {
  .stop .still, .stop.right .still { order: -1; width: min(80vw, 420px); max-height: 44vh; margin: 0 auto; }
  .no-3d .stop .wrap { flex-direction: column; align-items: center; align-items: safe center; }
}
@media (max-width: 860px) and (prefers-reduced-motion: reduce) {
  .stop .wrap { flex-direction: column; align-items: center; align-items: safe center; }
}
/* Tablet portrait: the panel is narrower than the screen, so it sits on the centre line with its subject
   above it (review item 53), not in a left column with dead space on the right. */
@media (min-width: 600px) and (max-width: 860px) {
  .stop .wrap, .stop.right .wrap { justify-content: center; }
}
@media (max-width: 1100px) and (prefers-reduced-motion: no-preference) {
  .motion-btn.pending + .btn.sm { margin-left: 0; }
}
/* Icon only up to 1100px (the full header needs about 920px), but the label stays the accessible name:
   visually hidden, never display: none. */
@media (max-width: 1100px) {
  .motion-btn { padding: 0 10px; }
  .motion-btn span span { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
}

/* Stop 8: labelled promotion (CAP 2.1/2.3) */
.promo-panel .promo-label { margin-bottom: 14px; }

/* Flat section: email + checklist */
.small { font-size: .95rem; }
.checklist-card { display: grid; gap: 10px; padding: clamp(22px, 3vw, 32px); border-radius: 24px; text-decoration: none; color: var(--on-night-2); background: linear-gradient(145deg, #111842, #1B2466 60%, #2A2F8F); box-shadow: 0 18px 48px rgba(15, 22, 46, .22); transition: transform .2s ease, box-shadow .2s ease; }
.checklist-card:hover { transform: translateY(-2px); box-shadow: 0 24px 60px rgba(15, 22, 46, .3); color: var(--on-night-2); }
.checklist-card .promo-label { justify-self: start; }
.checklist-card > .ic { width: 40px; height: 40px; color: var(--lavender); }
.checklist-card strong { font: 800 1.5rem/1.2 var(--font-display); color: #fff; }
.checklist-card .card-cta { display: inline-flex; align-items: center; gap: 8px; color: #fff; font-weight: 650; margin-top: 6px; }

/* Phones and touch screens: no backdrop blur behind the panels (it cost about 12 fps at DPR 3). The scrims
   above are dense enough on their own to keep AA against the brightest 3D frame (rule R6). */
@media (max-width: 860px), (pointer: coarse) {
  .has-3d .panel.on-night, .has-3d .stop-hero .panel { -webkit-backdrop-filter: none; backdrop-filter: none; }
}
/* social.css */
/* Follow row (app/views/partials/social.php). Rules per platform: docs/research/24-social-icons.md.
   The tile is ours; the logo inside it is untouched official artwork. Each SVG already carries the
   platform's clear space, so the logo image always fills the whole tile face.
   Hard rules kept here:
   - Tile faces are flat pure white (light) or pure black (dark). X and the TikTok Primary Note may only sit
     on white or black, so no tint, gradient or sheen may reach the area under a logo.
   - No filter, shadow, outline, rotation or animation is ever applied to the logo image itself. Only the
     tile (shadow and rim only, never position) and the focus ring change.
   - Minimum tile is 60px: Instagram glyph >= 29px with half-glyph clear space and TikTok Note >= 30px wide
     with 35% clear space only fit inside a tile from 60px up. */

.sf {
  --t: 60px;
  --sf-gap: 12px;
  --sf-radius: calc(var(--t) * .28);
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 16px var(--sf-gap);
}
.sf--lg { --t: 72px; --sf-gap: 16px; }
.sf--xl { --t: 96px; --sf-gap: 22px; }

.sf-item { flex: 0 0 auto; margin: 0; padding: 0; }

.sf-link {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  width: max(var(--t), 64px); /* equal columns, so tiles sit on an even rhythm whatever the label length */
  color: inherit;
  text-decoration: none;
  border-radius: var(--sf-radius);
  -webkit-tap-highlight-color: transparent;
}
.sf-link:focus { outline: none; }

.sf-tile {
  position: relative;
  display: block;
  width: var(--t);
  height: var(--t);
  border-radius: var(--sf-radius);
  background: #FFFFFF;
  box-shadow:
    0 1px 1px rgba(15, 22, 46, .05),
    0 2px 4px -1px rgba(15, 22, 46, .06),
    0 10px 22px -12px rgba(15, 22, 46, .34),
    inset 0 -1px 0 rgba(15, 22, 46, .05);
  transition: transform .18s cubic-bezier(.2, .7, .2, 1), box-shadow .22s ease;
  forced-color-adjust: none; /* keep the white/black face the marks depend on in Windows contrast themes */
}
/* Glass rim: a 1px ring, bright at the top, deeper at the bottom. Sits at the tile edge, inside the
   clear space, never over a logo. */
.sf-tile::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 1) 0%, rgba(15, 22, 46, .10) 55%, rgba(15, 22, 46, .20) 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
}
.sf-logo {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
}

.sf-label {
  font: 600 .78rem/1.2 var(--font-text, "Inter", system-ui, sans-serif);
  letter-spacing: .01em;
  color: var(--ink-2, #3E445B);
  white-space: nowrap;
  text-align: center; /* the two-line "Apps 365 on / LinkedIn" label stays centred under its tile */
  transition: color .18s ease;
}
.sf--lg .sf-label { font-size: .84rem; }
.sf--xl .sf-label { font-size: .92rem; }
/* "Apps 365 on" above the platform name for the parent company's page (review round 2, R2-43). No opacity, so it
   keeps the label's contrast. */
.sf-owner { display: block; font-weight: 500; }

.sf-link:hover .sf-tile {
  /* No movement: the official logo inside must never move or animate (Instagram rules, docs/research/24). */
  box-shadow:
    0 1px 1px rgba(15, 22, 46, .05),
    0 4px 8px -2px rgba(15, 22, 46, .08),
    0 18px 30px -14px rgba(15, 22, 46, .40),
    inset 0 -1px 0 rgba(15, 22, 46, .05);
}
.sf-link:hover .sf-label { color: var(--ink, #0F162E); }
.sf-link:focus-visible .sf-tile { outline: 3px solid var(--focus, #4554EA); outline-offset: 3px; }
.sf-link:focus-visible .sf-label { color: var(--ink, #0F162E); text-decoration: underline; text-underline-offset: .2em; }

/* Dark: flat pure black faces on a night background. The glass feel comes only from the rim and a 1px top
   highlight at the very edge; nothing tints the face, so X and the TikTok Note always sit on true black. */
.sf--dark .sf-tile {
  background: #000000;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .16),
    0 1px 0 rgba(255, 255, 255, .04),
    0 10px 24px -10px rgba(0, 0, 0, .85),
    0 0 0 1px rgba(0, 0, 0, .35);
}
.sf--dark .sf-tile::after {
  background: linear-gradient(180deg, rgba(255, 255, 255, .38) 0%, rgba(255, 255, 255, .10) 45%, rgba(255, 255, 255, .05) 100%);
}
.sf--dark .sf-label { color: var(--on-night-2, #B4B9D6); }
.sf--dark .sf-link:hover .sf-tile {
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .2),
    0 1px 0 rgba(255, 255, 255, .05),
    0 0 0 1px rgba(148, 140, 246, .35),
    0 16px 32px -12px rgba(69, 84, 234, .65);
}
.sf--dark .sf-link:hover .sf-label,
.sf--dark .sf-link:focus-visible .sf-label { color: #FFFFFF; }

/* Phones: five 60px tiles fit a 360px screen (324px of content) with 6px gaps. */
@media (max-width: 400px) {
  .sf { --sf-gap: 6px; }
  .sf-link { width: max(var(--t), 60px); }
  .sf-label { font-size: .74rem; }
}

/* Six links (Site::SOCIAL now includes LinkedIn) do not fit one row on a phone, and a 5 + 1 wrap leaves an
   orphan. When the row is too narrow for six (6 columns + 5 gaps at each size), lay them out 3 + 3 instead.
   .sf-wrap is the query container; browsers without container queries keep the plain wrapping row. */
.sf-wrap { container: sf / inline-size; }
@container sf (max-width: 443.98px) {
  .sf--md.sf--n6 { display: grid; grid-template-columns: repeat(3, max-content); column-gap: 20px; }
}
@container sf (max-width: 511.98px) {
  .sf--lg.sf--n6 { display: grid; grid-template-columns: repeat(3, max-content); column-gap: 24px; }
}
@container sf (max-width: 685.98px) {
  .sf--xl.sf--n6 { display: grid; grid-template-columns: repeat(3, max-content); column-gap: 28px; }
}

/* Windows contrast themes: the tile keeps its white/black face (forced-color-adjust: none above), so draw the
   keyboard focus ring in the theme's own highlight colour. */
@media (forced-colors: active) {
  .sf-link:focus-visible .sf-tile { outline-color: Highlight; }
}

@media (prefers-reduced-motion: reduce) {
  .sf-tile, .sf-label { transition: none; }
  .sf-link:hover .sf-tile, .sf-link:active .sf-tile { transform: none; }
}

@media print {
  .sf-tile { box-shadow: none; }
  .sf-tile::after { background: #BBBBBB; }
}
/* signup.css */
/* The Otto365 AI Brief by email: the sign-up form partial (app/views/partials/signup.php) and the
   /subscribe, /subscribe/confirm and /unsubscribe pages. Uses the site.css tokens only.
   Variants: .signup-light (on paper or white) and .signup-dark (on the night sections). */

.signup { margin: 0; max-width: 560px; }
.signup-fields { border: 0; margin: 0; padding: 0; min-width: 0; }
/* Only the field and the button fade while sign-up is unavailable: the label, the consent line and its still-working
   Privacy notice link (and its focus ring) keep full contrast (review round 2, R2-44). */
.signup-fields[disabled] .signup-row { opacity: .6; }
.signup-promise { margin: 0 0 14px; color: var(--ink-2); font-size: 1rem; line-height: 1.55; }
.signup-label { display: block; margin: 0 0 6px; font-weight: 650; font-size: .95rem; color: var(--ink); }
.signup-row { display: flex; gap: 10px; align-items: stretch; flex-wrap: wrap; }
.signup-input {
  flex: 1 1 240px; min-width: 0; min-height: 50px; padding: 0 16px;
  /* WCAG 1.4.11: the border is the only edge of the field, so it needs 3:1. #767C92 is 4.1:1 on white. */
  border: 1.5px solid #767C92; border-radius: 14px; background: #fff; color: var(--ink);
  font: 400 1.05rem/1.2 var(--font-text);
  transition: border-color .15s ease, box-shadow .15s ease;
}
.signup-input:hover { border-color: var(--ink-3); }
.signup-input:focus { outline: 3px solid var(--indigo); outline-offset: 2px; border-color: var(--indigo); }
.signup-input[aria-invalid="true"] { border-color: #B42318; }
.signup-btn { flex: 0 0 auto; min-height: 50px; justify-content: center; }
.signup-btn .ic { width: 1.05em; height: 1.05em; }
.signup-error { display: flex; gap: .5em; align-items: flex-start; margin: 10px 0 0; color: #B42318; font-weight: 600; font-size: .95rem; line-height: 1.45; }
.signup-error .ic { margin-top: .15em; }
.signup-consent { margin: 12px 0 0; font-size: .9rem; line-height: 1.5; color: var(--ink-3); }
.signup-consent a { color: var(--indigo-text); }
.signup-note { display: flex; gap: .5em; align-items: flex-start; margin: 0 0 12px; padding: 10px 14px; border-radius: 12px; background: var(--wash); color: var(--ink); font-size: .95rem; line-height: 1.45; }
.signup-note .ic { margin-top: .15em; color: var(--indigo-text); }
/* Honeypot: off-screen for people, still in the form for bots. Not display:none (some bots skip hidden fields). */
.signup-hp { position: absolute !important; left: -10000px; top: auto; width: 1px; height: 1px; overflow: hidden; }
@media (max-width: 480px) {
  .signup-btn { flex: 1 1 100%; }
}

/* Dark variant */
.signup-dark .signup-promise { color: var(--on-night-2); }
.signup-dark .signup-label { color: var(--on-night); }
.signup-dark .signup-input { background: rgba(255, 255, 255, .06); border-color: rgba(238, 240, 252, .55); color: #fff; }
.signup-dark .signup-input:hover { border-color: rgba(238, 240, 252, .75); }
.signup-dark .signup-input:focus { outline-color: var(--lavender); border-color: var(--lavender); }
.signup-dark .signup-input[aria-invalid="true"] { border-color: #FDA29B; }
.signup-dark .signup-error { color: #FDA29B; }
.signup-dark .signup-consent { color: var(--on-night-2); }
.signup-dark .signup-consent a { color: var(--on-night); }
.signup-dark .signup-note { background: rgba(255, 255, 255, .08); color: var(--on-night); }
.signup-dark .signup-note .ic { color: var(--lavender); }

/* Email pages */
.sub-grid { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: clamp(28px, 5vw, 64px); align-items: start; }
.sub-head { margin-bottom: 24px; }
.sub-head h1 { font-size: clamp(2rem, 1.4rem + 2.6vw, 3.2rem); }
.sub-head .lead { max-width: 46ch; }
.sub-card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: clamp(20px, 3.5vw, 32px); scroll-margin-top: calc(var(--header-h) + 16px); }
.sub-done-line { display: flex; gap: 12px; align-items: flex-start; margin: 0 0 12px; color: var(--ink); font-size: 1.05rem; line-height: 1.55; }
.sub-done-line .ic { flex: none; width: 28px; height: 28px; padding: 5px; border-radius: 999px; background: #E3F4EC; color: var(--ok); margin-top: 0; }
.sub-small { font-size: .92rem; color: var(--ink-3); margin: 0; }
.sub-get { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: clamp(22px, 3.5vw, 32px); }
.sub-get h2 { font-size: clamp(1.3rem, 1.1rem + .8vw, 1.7rem); }
.sub-list { list-style: none; margin: 0 0 18px; padding: 0; display: grid; gap: 16px; }
.sub-list li { display: grid; grid-template-columns: 40px 1fr; gap: 12px; align-items: start; color: var(--ink-2); line-height: 1.55; }
.sub-list strong { display: block; color: var(--ink); font-family: var(--font-display); font-weight: 700; font-size: 1.02rem; margin-bottom: 2px; }
.sub-ic { display: grid; place-items: center; width: 40px; height: 40px; border-radius: 12px; background: var(--wash); color: var(--indigo-text); }
.sub-ic .ic { width: 20px; height: 20px; }
.sub-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 8px; }
.sub-follow { margin-top: clamp(40px, 6vw, 72px); }
.sub-grid .sub-follow { margin-top: 0; }
.sub-follow h2 { font-size: clamp(1.3rem, 1.1rem + .8vw, 1.7rem); }
.sub-follow p { color: var(--ink-2); }
.sub-links { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px; }
.sub-links .btn { min-height: 44px; }
.sub-promo {
  margin-top: clamp(40px, 6vw, 72px); border-radius: 26px; padding: clamp(24px, 4vw, 44px);
  background: linear-gradient(135deg, #111842, #1B2466 60%, #2A2F8F); color: var(--on-night);
}
.sub-promo h2 { color: #fff; max-width: 24ch; font-size: clamp(1.4rem, 1.15rem + 1.1vw, 2rem); }
.sub-promo p { color: var(--on-night-2); max-width: 60ch; }
.sub-promo-label { display: inline-block; font: 700 .75rem/1 var(--font-text); letter-spacing: .14em; text-transform: uppercase; color: #fff; background: rgba(255, 255, 255, .12); padding: 7px 12px; border-radius: 999px; }
.sub-promo .btn.ghost { color: #fff; border-color: rgba(255, 255, 255, .38); }
.sub-promo .btn.ghost:hover { background: rgba(255, 255, 255, .1); color: #fff; }
/* The promotion's picture, Otto365 the builder (review round 2, R2-53: the card's right side was empty). The two
   columns apply only when the picture is in the markup (<img class="sub-promo-art"> in subscribe-promo.php); below
   900px the picture is not shown. */
.sub-promo-art { display: block; width: min(100%, 360px); height: auto; margin-top: 18px; }
.sub-promo:has(> .sub-promo-art) { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 36%); column-gap: 32px; align-items: center; }
.sub-promo:has(> .sub-promo-art) > :not(.sub-promo-art) { grid-column: 1; }
/* A grid item stretches to its column; the label stays a pill the width of its words, as below 900px. */
.sub-promo:has(> .sub-promo-art) > .sub-promo-label { justify-self: start; }
.sub-promo:has(> .sub-promo-art) > .sub-promo-art { grid-column: 2; grid-row: 1 / span 4; width: 100%; margin: 0; }
@media (max-width: 900px) {
  .sub-promo:has(> .sub-promo-art) { display: block; }
  .sub-promo-art { display: none; }
}
.sub-narrow { max-width: 680px; }
@media (max-width: 900px) {
  .sub-grid { grid-template-columns: 1fr; }
}
/* pop.css */
/* Otto365 "warm pop": the AI palette shared with apps365.ai (Peter, 10 Oct 2026: "give the otto365.ai site the same
   colour palette as the updated apps365.ai - make it pop, this is our new colour scheme for AI related").
   apps365.ai's layer is /assets/css/warm-pop.css: Kai's Warm Sunrise orange #FF5A2C, a violet -> orange sunset
   gradient and warm cream light surfaces. css_links() (app/lib/helpers.php) loads this file LAST in every bundle,
   so it wins over the page styles; taking it out of css_links() puts the indigo palette back.
   The site promises WCAG 2.2 AA (/accessibility), so every pair below was measured:
   - white on the button orange #D93C0E -> #C4330A: 4.56 to 5.49:1 (the brighter #FF5A2C is only 3.1:1 with white,
     so it is used for glows, rules and large shapes, never under small text);
   - --orange-text #B83A0A: 5.47:1 on the cream paper, 5.07:1 on the orange wash;
   - --peach #FFB08A on the night: 10.7:1; #FF8A4C on the story glass #141B46: 7.05:1;
   - the hero's gradient words end at #C93D0C: 4.7:1 on the paper and 3.9:1 at worst, when the dark studio screen passes
     behind the 90%-opaque right half of the hero panel during the camera move (large text needs 3:1; #E04512 gave 2.9:1). */
:root {
  --orange: #FF5A2C;
  --orange-2: #FF7A45;
  --orange-3: #FF8A4C;
  --orange-cta: #D93C0E;
  --orange-cta-2: #C4330A;
  --orange-cta-hover: #C2340B;
  --orange-cta-hover-2: #B52F08;
  --orange-text: #B83A0A;
  --orange-wash: #FFEDE4;
  --peach: #FFB08A;
  --cream: #FFF6EA;
  --sunset: linear-gradient(120deg, #4554EA 0%, #7A4DEF 36%, #FF5A2C 76%, #FF8A4C 100%);
  --sunset-text: linear-gradient(100deg, #4554EA 0%, #7A4DEF 45%, #C93D0C 100%);
  --sunset-night: linear-gradient(100deg, #A99BFF 0%, #FF9E6E 65%, #FFB08A 100%);
  --cta-glow: 0 12px 26px -10px rgba(255, 90, 44, .65);
  /* Warm light surfaces in place of the cool blue-grey (the studio backdrop in home3d.js matches: #FFF3EA). */
  --paper: #FFF8F2;
  --line: #F1E4DA;
  --wash: #FFEDE4;
}
::selection { background: #FFD3BD; color: #1B1240; }

/* ---------- Buttons: the one filled CTA per view is orange, with a warm glow ---------- */
.btn:not(.ghost) { border-color: transparent; background: linear-gradient(120deg, var(--orange-cta) 0%, var(--orange-cta-2) 100%); box-shadow: var(--cta-glow), inset 0 1px 0 rgba(255, 255, 255, .18); }
.btn:not(.ghost):hover { background: linear-gradient(120deg, var(--orange-cta-hover) 0%, var(--orange-cta-hover-2) 100%); box-shadow: 0 18px 34px -10px rgba(255, 90, 44, .75), inset 0 1px 0 rgba(255, 255, 255, .18); }
.btn.ghost:hover { border-color: var(--orange-text); color: var(--ink); }
.on-night .btn.ghost:hover, .sub-promo .btn.ghost:hover { border-color: var(--peach); color: #fff; }

/* ---------- Header ---------- */
.site-header.solid, .page .site-header, .home .site-header { background: rgba(255, 248, 242, .86); }
.nav a:hover, .nav a[aria-current="page"], .nav a[aria-current="true"] { background: rgba(255, 90, 44, .09); }
.nav a[aria-current="page"], .nav a[aria-current="true"] { box-shadow: inset 0 -2px 0 var(--orange-2); }
/* AI Guides: the apps365.ai free guides, in the header as on apps365.ai (orange, so it reads as the offer). */
/* #9E3208, not --orange-text: the light header is 86% opaque, and over a dark card scrolling under it (.promo-inline on
   episode pages) #B83A0A fell to 4.05:1; #9E3208 keeps 5.06:1 there and 6.85:1 on the cream (review, 10 Oct 2026). */
.nav a.nav-guides { display: inline-flex; align-items: center; gap: 6px; color: #9E3208; font-weight: 650; }
.nav a.nav-guides .ic { width: 1.05em; height: 1.05em; }
.nav a.nav-guides:hover { color: #8A2B06; background: rgba(255, 90, 44, .1); }
.site-header.on-dark .nav a.nav-guides { color: var(--peach); }
.site-header.on-dark .nav a.nav-guides:hover { color: #fff; background: rgba(255, 138, 76, .16); }
@media (max-width: 1100px) {
  /* The open menu is a paper panel (site.css forces ink on its links); the guides link keeps its orange. */
  .nav a.nav-guides { color: #9E3208 !important; }
}

/* ---------- Light surfaces: accents turn warm ---------- */
.eyebrow { color: var(--orange-text); }
.chip { background: var(--orange-wash); color: var(--orange-text); }
.chip.ai { background: #F3EEFF; color: var(--ink-2); }
.callout .ic, .signup-note .ic, .step-row .ic, .how-made .ic, .story h2 .ic, .meta .ic { color: var(--orange-text); }
.sub-ic { background: var(--orange-wash); color: var(--orange-text); }
.ep-meta { color: var(--orange-text); }
.ep-card { transition: box-shadow .2s ease, transform .2s ease; }
.ep-card:hover { transform: translateY(-2px); box-shadow: 0 1px 2px rgba(15, 22, 46, .06), 0 16px 36px -8px rgba(255, 90, 44, .28); }
.ep-thumb { box-shadow: 0 0 0 1px rgba(255, 122, 69, .25); }
.ep-nav-item:hover { box-shadow: 0 1px 2px rgba(15, 22, 46, .06), 0 16px 36px -8px rgba(255, 90, 44, .26); }
.cite:hover { background: var(--orange-cta); color: #fff; }
.do-card { background: linear-gradient(135deg, #FFF3EA, #FFE6D8); border-left-color: var(--orange-cta); }
.quotes blockquote, .promo-inline-transcript { border-left-color: var(--orange-2); }
.promo-inline-transcript .promo-label { color: var(--orange-text); }
.prose a, .signup-consent a { text-decoration-color: rgba(255, 90, 44, .55); }
.prose a:hover { color: var(--orange-text); }

/* The hero headline's last words in the sunset gradient (large text: 3:1 is the AA bar, the darkest end is 3.97:1). */
.pop-text { background: var(--sunset-text); -webkit-background-clip: text; background-clip: text; color: transparent; -webkit-box-decoration-break: clone; box-decoration-break: clone; }
@media (forced-colors: active) { .pop-text { background: none; color: CanvasText; } }

/* ---------- Home: the studio (light) ---------- */
/* Without the canvas (and on Pause) the hero paints the studio's own warm backdrop, with the sunset glows of apps365.ai. */
.stop-hero {
  background:
    radial-gradient(900px 560px at -8% -10%, rgba(255, 122, 69, .20), transparent 62%),
    radial-gradient(760px 520px at 108% 4%, rgba(91, 77, 255, .12), transparent 60%),
    radial-gradient(120% 90% at 75% 45%, #FFFFFF 0%, #FFF3EA 55%, #FCE9DC 100%);
}
.has-3d .stop-hero .panel { background: linear-gradient(90deg, rgba(255, 248, 242, .93), rgba(255, 248, 242, .90)); box-shadow: inset 0 0 0 1px rgba(255, 138, 76, .16); }
@media (max-width: 860px) { .has-3d .stop-hero .panel { background: var(--paper); } }
.today { background: rgba(255, 255, 255, .78); border-color: var(--line); }
.today-title:hover { color: var(--orange-text); }

/* ---------- Home: the night stops ---------- */
.stop[data-theme="dark"] { background: radial-gradient(70% 60% at 88% 100%, rgba(255, 90, 44, .16), transparent 70%), radial-gradient(110% 80% at 60% 50%, #1A2250 0%, var(--night) 70%); }
/* As in home.css, every stop is see-through over the canvas: the two rules above (same specificity, later file) must
   not cover the 3D scene; on Pause the stops paint their flat backdrops again. */
.has-3d .stop { background: transparent; }
.has-3d.motion-paused .stop-hero { background: #FFF3EA; }
.has-3d.motion-paused .stop[data-theme="dark"] { background: var(--night); }
.has-3d .panel.on-night { box-shadow: inset 0 0 0 1px rgba(148, 140, 246, .16), inset 0 1px 0 rgba(255, 138, 76, .38); }
.on-night .eyebrow { color: var(--peach); }
.link-night { color: var(--peach); }
.link-night:hover { color: #fff; }
.proof .ic { color: var(--orange-3); }
.ep-list li { box-shadow: inset 2px 0 0 rgba(255, 122, 69, .55); }
.ep-list .num { color: var(--peach); }
.stop .still-poster { box-shadow: 0 0 0 1px rgba(255, 138, 76, .35), 0 24px 60px rgba(0, 0, 0, .4); }

/* ---------- Dark promotion cards: an orange glow rising from the bottom corner ---------- */
.checklist-card, .sub-promo, .promo-inline {
  background:
    radial-gradient(520px 300px at 100% 110%, rgba(255, 90, 44, .30), transparent 70%),
    linear-gradient(145deg, #111842, #1B2466 60%, #2A2F8F);
}
.checklist-card > .ic, .promo-tile .ic { color: var(--orange-3); }
.checklist-card:hover { box-shadow: 0 24px 60px -12px rgba(255, 90, 44, .42); }
.checklist-card .card-cta { color: var(--peach); }
.promo-tile:hover { box-shadow: inset 0 0 0 1px rgba(255, 138, 76, .45); }

/* ---------- Social tiles on the night ---------- */
.sf--dark .sf-link:hover .sf-tile { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .2), 0 1px 0 rgba(255, 255, 255, .05), 0 0 0 1px rgba(255, 138, 76, .5), 0 16px 32px -12px rgba(255, 90, 44, .6); }

/* ---------- Footer: a sunset rule along the top, warm headings ---------- */
.site-footer { background: radial-gradient(800px 360px at 92% 0%, rgba(255, 90, 44, .13), transparent 70%), var(--night); }
.site-footer::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: var(--sunset); }
.foot-grid h2 { color: var(--peach); }
.site-footer a:hover { color: var(--peach); }
.site-footer a.foot-guides { color: var(--peach); font-weight: 650; }
.site-footer a.foot-guides:hover { color: #fff; }

/* The header gained a link (AI Guides), so between the Menu breakpoint (1100px, site.css) and 1320px the Pause button
   shows its icon only, as it already does below 1100px (home.css; its label stays the accessible name), and the
   links sit a little closer. Up to 1240px "by Apps 365" is visually hidden too (still part of the link's name, so
   the visible "AI services" starts the accessible name, WCAG 2.5.3). Measured with WCAG 1.4.12 text spacing applied
   through a constructed stylesheet (an injected <style> is blocked by the CSP, which made an earlier check pass
   falsely): it fits from 1100.5px. */
@media (width > 1100px) and (max-width: 1240px) {
  .nav a.nav-out .nav-long { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
}
@media (width > 1100px) and (max-width: 1320px) {
  .nav { gap: 0; }
  .nav a { padding: 8px 10px; }
  .site-header .wrap { gap: 16px; }
  .motion-btn { padding: 0 10px; }
  .motion-btn span span { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
}

/* Inner pages: the On Air tile (site.css) in the sunset colours, with a warm glow. */
.wrap:has(> .page-head)::before {
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 240' fill='none'%3E%3Crect x='2' y='2' width='236' height='236' rx='37' stroke='%234554EA' stroke-opacity='.14' stroke-width='1.5'/%3E%3Crect x='26' y='26' width='188' height='188' rx='29.5' stroke='%237A4DEF' stroke-opacity='.22' stroke-width='1.75'/%3E%3Crect x='47' y='47' width='146' height='146' rx='23' stroke='%23FF5A2C' stroke-opacity='.32' stroke-width='2'/%3E%3Crect x='65' y='65' width='110' height='110' rx='17' stroke='%237A4DEF' stroke-opacity='.4' stroke-width='2.25'/%3E%3Crect x='80' y='80' width='80' height='80' rx='12.5' stroke='%23FF5A2C' stroke-opacity='.6' stroke-width='2.5'/%3E%3Cg fill='%23E9441A' fill-opacity='.6'%3E%3Crect x='99' y='99' width='18' height='18' rx='4.5'/%3E%3Crect x='123' y='99' width='18' height='18' rx='4.5'/%3E%3Crect x='99' y='123' width='18' height='18' rx='4.5'/%3E%3Crect x='124' y='130' width='16' height='4' rx='2'/%3E%3Crect x='130' y='124' width='4' height='16' rx='2'/%3E%3C/g%3E%3C/svg%3E") center / contain no-repeat,
    radial-gradient(closest-side, rgba(255, 122, 69, .16), rgba(255, 122, 69, 0));
}
/* Episode pages: the headline's kicker ("OpenAI:") in the sunset gradient (large text, 3.97:1 at its darkest end). */
.ep-head .kicker { background: var(--sunset-text); -webkit-background-clip: text; background-clip: text; color: transparent; }
@media (forced-colors: active) { .ep-head .kicker { background: none; color: CanvasText; } }
