/* =========================================================================
   daidocs.com: "2040" enhancement layer. Loads AFTER core.css and only on
   this build. Everything here is additive polish: depth, light, motion and
   micro-interaction. No layout depends on it, and every effect degrades to
   the core.css appearance if the feature or JS is unavailable.
   ========================================================================= */

/* animatable angle for conic borders (progressive: ignored if unsupported) */
@property --angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

:root {
  --beam: color-mix(in oklab, var(--accent) 55%, transparent);
  --beam2: color-mix(in oklab, var(--accent2) 55%, transparent);
}

/* ---------------- scroll progress ---------------- */

.scroll-progress {
  position: fixed; top: 0; left: 0; height: 2px; z-index: 90;
  width: 100%; transform-origin: 0 50%; transform: scaleX(0);
  background: linear-gradient(90deg, var(--accent), var(--accent2), var(--accent));
  box-shadow: 0 0 12px var(--accent);
  pointer-events: none;
}

/* ---------------- volumetric hero ---------------- */

.hero { perspective: 1200px; }

/* light beams raking across the hero */
.hero::before {
  content: ""; position: absolute; inset: -20% -10% auto -10%; height: 150%;
  background:
    linear-gradient(102deg, transparent 42%, var(--beam) 49%, transparent 56%),
    linear-gradient(78deg, transparent 60%, var(--beam2) 66%, transparent 72%);
  opacity: .09; filter: blur(22px);
  animation: rake 18s ease-in-out infinite alternate;
  pointer-events: none;
}
@keyframes rake {
  0%   { transform: translate3d(-6%, 0, 0) rotate(-1.5deg); opacity: .06; }
  100% { transform: translate3d(6%, 0, 0) rotate(1.5deg); opacity: .13; }
}

/* horizon glow at the section seam */
.hero::after { height: 2px; filter: blur(.5px); opacity: .7; }

/* ---------------- headline: word-by-word rise ---------------- */

.js-fx h1 .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.5em) rotateX(-38deg);
  transform-origin: 50% 100%;
  animation: word-rise .78s cubic-bezier(.2,.9,.25,1) forwards;
  animation-delay: calc(var(--i) * 46ms);
}
@keyframes word-rise { to { opacity: 1; transform: none; } }
h1, h2 { text-wrap: balance; }

/* ---------------- cursor spotlight + tilt on panels ---------------- */

.card, .stat, .tier, .viz, .zone {
  position: relative;
  transform-style: preserve-3d;
  will-change: transform;
}

/* the spotlight follows the pointer via --mx/--my written by fx-2040.js */
.card::after, .stat::after, .tier::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: radial-gradient(320px circle at var(--mx, 50%) var(--my, 0%),
              color-mix(in oklab, var(--accent) 12%, transparent), transparent 62%);
  opacity: 0; transition: opacity .35s ease; pointer-events: none;
}
.card:hover::after, .stat:hover::after, .tier:hover::after { opacity: 1; }

.fx-tilt {
  transform: perspective(900px)
             rotateX(var(--rx, 0deg))
             rotateY(var(--ry, 0deg))
             translateY(var(--ty, 0px));
  transition: transform .16s ease-out;
}
.fx-tilt.fx-rest { transition: transform .5s cubic-bezier(.22,1,.36,1); }

/* ---------------- conic gradient border on key surfaces ---------------- */

@supports (background: linear-gradient(in oklab, red, blue)) {
  .install, .tier.featured, .honesty {
    position: relative;
  }
  .install::before, .tier.featured::before, .honesty::after {
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    padding: 1px; pointer-events: none;
    background: conic-gradient(from var(--angle),
      transparent 0turn, var(--accent) .12turn, transparent .3turn,
      transparent .55turn, var(--accent2) .68turn, transparent .85turn);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor; mask-composite: exclude;
    opacity: .8;
    animation: spin-angle 9s linear infinite;
  }
  @keyframes spin-angle { to { --angle: 1turn; } }
}

/* ---------------- stat tiles: depth + live counter ---------------- */

.stat { overflow: hidden; }
.stat .num { font-variant-numeric: tabular-nums; }
.stat::before { height: 2px; filter: blur(.3px); }
.stat:hover { border-color: color-mix(in oklab, var(--accent) 45%, transparent); }

/* faint scan sweep on hover */
.stat .num::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(100deg, transparent 40%,
              color-mix(in oklab, var(--accent) 16%, transparent) 50%, transparent 60%);
  transform: translateX(-100%); transition: transform .9s ease;
}
.stat:hover .num::after { transform: translateX(100%); }

/* ---------------- charts: sheen + draw ---------------- */

.bar-fill { position: relative; overflow: hidden; }
.bar-fill::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 30%, rgba(255,255,255,.28) 50%, transparent 70%);
  transform: translateX(-100%);
  animation: sheen-pass 2.6s ease-in-out 1.1s infinite;
}
@keyframes sheen-pass { 0% { transform: translateX(-100%); } 55%, 100% { transform: translateX(220%); } }

/* ---------------- format icon: orbit + float ---------------- */

/* No 3D rotation on the icon: a rotateX/rotateY forces the browser to snapshot
   the image into a fixed-size texture, which visibly softens it. The vertical
   float composites without re-rasterising, so the artwork stays razor sharp. */
.fmt-icon img {
  animation: hover-y 7s ease-in-out infinite;
  image-rendering: auto;
}
.fmt-icon::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 260px; height: 260px; margin: -130px 0 0 -130px;
  border: 1px solid color-mix(in oklab, var(--accent) 22%, transparent);
  border-radius: 50%;
  animation: orbit 26s linear infinite;
  pointer-events: none;
}
@keyframes orbit { to { transform: rotate(1turn) scale(1.04); } }

/* A light travelling the orbit ring. The wrapper carries the *same* `orbit`
   animation as .fmt-icon::before, so the star stays welded to the ring line
   through both the rotation and its 1.04 scale breath. Animating it
   separately would let the two drift apart over a 26s cycle. */
.orbit-star {
  position: absolute; left: 50%; top: 50%;
  width: 260px; height: 260px; margin: -130px 0 0 -130px;
  border-radius: 50%;
  animation: orbit 26s linear infinite;
  pointer-events: none;
}
/* the comet trail: a conic sweep clipped to the ring band, so it reads as
   light left behind on the line rather than a disc */
.orbit-star::before {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: conic-gradient(from 0deg,
    transparent 0deg,
    transparent 288deg,
    color-mix(in oklab, var(--accent) 30%, transparent) 336deg,
    color-mix(in oklab, var(--accent) 85%, transparent) 357deg,
    var(--accent) 360deg);
  -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 3px), #000 calc(100% - 2px));
  mask: radial-gradient(closest-side, transparent calc(100% - 3px), #000 calc(100% - 2px));
  filter: blur(.6px);
  opacity: .9;
}
/* the star itself, sitting on the ring at 12 o'clock where the trail ends */
.orbit-star::after {
  content: ""; position: absolute; top: -5px; left: 50%; margin-left: -5px;
  width: 10px; height: 10px; border-radius: 50%;
  background: radial-gradient(circle, #fff 0%, #eafff6 38%, var(--accent) 100%);
  box-shadow:
    0 0 6px 1px var(--accent),
    0 0 16px 5px color-mix(in oklab, var(--accent) 55%, transparent),
    0 0 34px 12px color-mix(in oklab, var(--accent) 22%, transparent);
  animation: star-twinkle 3.1s ease-in-out infinite;
}
@keyframes star-twinkle {
  0%, 100% { transform: scale(1);    opacity: 1; }
  50%      { transform: scale(1.28); opacity: .82; }
}

/* ---------------- ticker chips: living dots ---------------- */

.chip i { animation: pulse-dot 2.6s ease-in-out infinite; }
.chip:nth-child(2n) i { animation-delay: -.8s; }
.chip:nth-child(3n) i { animation-delay: -1.6s; }
@keyframes pulse-dot {
  0%,100% { box-shadow: 0 0 6px var(--accent); opacity: .75; }
  50%     { box-shadow: 0 0 14px var(--accent); opacity: 1; }
}

/* ---------------- nav + buttons: magnetic feel ---------------- */

.btn, .nav-cta, .t-cta { position: relative; overflow: hidden; }
.btn.primary::after, .nav-cta::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 35%, rgba(255,255,255,.35) 50%, transparent 65%);
  transform: translateX(-120%); transition: transform .7s ease;
}
.btn.primary:hover::after, .nav-cta:hover::after { transform: translateX(120%); }

/* ---------------- terminal: caret while typing ---------------- */

.term.is-typing pre::after {
  content: "▋"; color: var(--accent);
  animation: caret 1s steps(1) infinite;
}
@keyframes caret { 50% { opacity: 0; } }

/* ---------------- section seams ---------------- */

section.band { position: relative; }
section.band::after {
  content: ""; position: absolute; left: 50%; bottom: -1px; transform: translateX(-50%);
  width: min(1140px, 92vw); height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in oklab, var(--accent) 38%, transparent), transparent);
  opacity: .5; pointer-events: none;
}

/* ---------------- reduced motion: strip all of it ---------------- */

@media (prefers-reduced-motion: reduce) {
  .hero::before, .fmt-icon img, .fmt-icon::before, .chip i,
  .orbit-star, .orbit-star::after,
  .bar-fill::after, .install::before, .tier.featured::before, .honesty::after,
  .js-fx h1 .w {
    animation: none !important;
  }
  .js-fx h1 .w { opacity: 1; transform: none; }
  .fx-tilt { transform: none !important; }
  .scroll-progress { display: none; }
}


/* ---------------- charts: 2040 bar treatment ----------------
   The comparison bars are the single most-looked-at graphic on the site, and a
   flat rectangle in two greys makes a 3.75x difference look like a rounding
   error. Depth, a lit edge and a colour that carries meaning do the work the
   caption otherwise has to do alone. Ours reads as the brand accent; the
   comparison arm reads warm, so "more" looks expensive rather than merely
   longer. */

.viz .bars { gap: 17px; }

.bar-track {
  height: 34px;
  border-radius: 11px;
  background:
    linear-gradient(180deg, rgba(255,255,255,.035) 0%, rgba(255,255,255,0) 55%),
    var(--track);
  box-shadow:
    inset 0 0 0 1px var(--hairline),
    inset 0 2px 5px rgba(0,0,0,.35);
  overflow: visible;
}

/* Faint measure lines behind the fill, so a bar's length is readable as a
   quantity and not only as "longer than the other one". */
.bar-track::before {
  content: ""; position: absolute; inset: 1px 1px 1px 1px; border-radius: 10px;
  background: repeating-linear-gradient(90deg,
    transparent 0 calc(25% - 1px), var(--hairline) calc(25% - 1px) 25%);
  opacity: .5; pointer-events: none;
}

.bar-fill {
  top: 4px; bottom: 4px; left: 4px;
  border-radius: 8px;
  overflow: hidden;
}

/* Lit top edge: the single cheapest cue that a shape has volume. */
.bar-fill::before {
  content: ""; position: absolute; left: 0; right: 0; top: 0; height: 45%;
  background: linear-gradient(180deg, rgba(255,255,255,.22), rgba(255,255,255,0));
  border-radius: 8px 8px 0 0; pointer-events: none; z-index: 1;
}

/* Ours: brand accent, lit from the left, with a glow that reads as "this is
   the good one" before the label is read. */
.s-aidoc {
  background: linear-gradient(96deg, #2fe0a8 0%, #4ade87 38%, #38bdf8 100%);
  box-shadow:
    0 0 26px rgba(74, 222, 135, .38),
    0 2px 12px rgba(0, 0, 0, .45);
}

/* The comparison arm: warm, so length reads as cost. Deliberately slightly
   lower contrast than ours so the eye lands on the short bar first. */
.s-full {
  background: linear-gradient(96deg, #f7a13c 0%, #ef7a2b 45%, #d9432c 100%);
  box-shadow:
    0 0 22px rgba(233, 118, 46, .26),
    0 2px 12px rgba(0, 0, 0, .45);
}

.s-live  { background: linear-gradient(96deg, #34d399 0%, #14b8a6 100%); box-shadow: 0 0 20px rgba(45, 212, 191, .26), 0 2px 12px rgba(0,0,0,.45); }
.s-rival { background: linear-gradient(96deg, #b79cff 0%, #7c5cf0 100%); box-shadow: 0 0 20px rgba(148, 122, 255, .26), 0 2px 12px rgba(0,0,0,.45); }
.s-none  { background: linear-gradient(96deg, #7b8494 0%, #4b5563 100%); }

/* Value as a chip rather than loose text, so it holds its own against the
   bar it sits beside. */
.bar-val {
  font-size: 12.5px; font-weight: 600; letter-spacing: .02em;
  padding: 3px 9px; margin-left: 10px; border-radius: 7px;
  background: color-mix(in oklab, var(--bg) 72%, transparent);
  border: 1px solid var(--hairline);
  backdrop-filter: blur(6px);
  z-index: 2;
}
/* Inside a full-width bar the chip would fight the fill, so that variant stays
   flat and simply sits on the colour. */
.bar-val.in {
  background: none; border: none; backdrop-filter: none;
  padding: 0; margin-left: 0; right: 13px;
  color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.55);
}

.bar-row .lbl strong { letter-spacing: -.01em; }

@media (prefers-reduced-motion: reduce) {
  .bar-fill::after { animation: none; }
}
@media (max-width: 560px) {
  .bar-track { height: 30px; }
  .bar-val { font-size: 11.5px; padding: 2px 7px; }
}

/* The one-line takeaway under a comparison chart. Sized between the bars and
   the footnote: it is the sentence a reader should leave with if they read
   nothing else in the block. */
.viz-gap {
  margin: 16px 0 0; padding: 11px 14px;
  font-size: 13.5px; color: var(--text-2);
  background: color-mix(in oklab, var(--accent) 7%, transparent);
  border: 1px solid var(--accent-b);
  border-radius: 10px;
}
.viz-gap b { color: var(--text-hi); }

/* The store tree is ASCII art: it cannot reflow, so if the column is narrower
   than the widest line the annotations scroll out of sight and the diagram
   silently loses its labels. Scale the type to the space instead of clipping. */
.term pre.tree {
  font-size: clamp(10.5px, 1.15vw, 13px);
  line-height: 1.7;
  padding-left: 18px; padding-right: 18px;
}
@media (max-width: 620px) { .term pre.tree { font-size: 10px; } }


/* ---------------- cross-document view transitions ----------------
   This is a multi-page site, so every nav click is a real page load and the
   header repaints with it. That reads as the whole chrome reloading even
   though only the content changed.

   `navigation: auto` opts the site into cross-document view transitions, and
   naming the header and footer makes the browser treat them as the SAME
   element across the two documents: it holds them still and cross-fades only
   what actually differs. No SPA, no router, no framework. Browsers without
   support (currently Firefox) simply navigate as before.

   The names must be unique per document, which is why only the single header,
   footer and main region carry them. */

@view-transition { navigation: auto; }

.site-header { view-transition-name: site-header; }
.site-footer { view-transition-name: site-footer; }
main#main     { view-transition-name: page-main; }

/* The header and footer are meant to look static, so they get no animation:
   naming them alone is what pins them in place. Only the content region
   animates, and gently. */
::view-transition-group(site-header),
::view-transition-group(site-footer) { animation-duration: 0s; }

::view-transition-old(page-main) {
  animation: vt-out .16s cubic-bezier(.4,0,1,1) both;
}
::view-transition-new(page-main) {
  animation: vt-in .22s cubic-bezier(0,0,.2,1) both;
}
@keyframes vt-out { to { opacity: 0; transform: translateY(-4px); } }
@keyframes vt-in  { from { opacity: 0; transform: translateY(6px); } }

/* The ambient background is fixed and identical on every page; letting it
   cross-fade makes the aurora visibly flicker on each navigation. */
.field { view-transition-name: ambient-field; }
::view-transition-group(ambient-field) { animation-duration: 0s; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(page-main),
  ::view-transition-new(page-main) { animation-duration: 0s; }
}


