# Akta hero 01 default

A shadcn/ui block from the [@craftui](https://www.craftui.space) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/craftui/akta-hero-01-default).

- Page, with a live preview: https://shadcn.exchange/r/craftui/akta-hero-01-default
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/craftui/akta-hero-01-default-light-43d48480e7b84729.png
- Registry item: https://www.craftui.space/r/akta-hero-01-default.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://www.craftui.space/r/akta-hero-01-default.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@hugeicons/core-free-icons`, `@hugeicons/react`, `@tabler/icons-react`, `clsx`, `motion`, `tailwind-merge`
- Registry items: `https://www.craftui.space/r/craftui-base.json`, `utils`

## Files

### `components/craftui/sections/akta-hero-01/default.tsx`

```tsx
"use client";

import React, {
  useEffect,
  useRef,
  useState,
  useSyncExternalStore,
} from "react";
import {
  AnimatePresence,
  motion,
  useInView,
  useReducedMotion,
} from "motion/react";
import { HugeiconsIcon } from "@hugeicons/react";
import { ArrowUpRight01Icon } from "@hugeicons/core-free-icons";
import {
  IconArrowUpRight,
  IconChartBar,
  IconChevronDown,
  IconCornerDownRight,
  IconFileDescription,
  IconFilter,
} from "@tabler/icons-react";

import { cn } from "@/lib/utils";

/* ---------------------------------------------------------------------------
 * akta-hero-01 — "technical drawing on white paper"
 *
 * Every value lives in this file. Nothing is written into globals.css except
 * the six device classes, which do things a utility cannot express: the notch's
 * masked pseudo-element, the hatch gradient, the rolling label's three
 * coordinated elements, the tick field's indexed stagger, the plate, and the
 * entrance keyframes.
 *
 * There is no `@theme` block and no `:root` palette, so nothing here can
 * collide with — or be re-skinned by — the host's stylesheet, and no class can
 * silently do nothing because a token was never installed. The trade is that
 * dark mode is carried by a `dark:` twin on every colour rather than by
 * swapping one variable, which is why the tokens below are grouped constants
 * rather than repeated at each call site.
 *
 * Every className is assembled with `cn()` in a fixed order so a given concern
 * is always in the same argument:
 *
 *     cn( layout , type , colour , state , device )
 *
 * Read design.md (shipped alongside) before building a matching section.
 * ------------------------------------------------------------------------ */

/* ── Type ─────────────────────────────────────────────────────────────────
 * Size, leading, tracking and weight together — one constant per role.
 * Tracking is in `em`, so a single value stays correct at every step of a
 * responsive ramp: -0.06em is -6% of whatever the size currently is, which is
 * the face's own rule. Only the top display step is tightened by hand. */
const FONT_SANS =
  "font-[family-name:var(--font-geist-sans),ui-sans-serif,system-ui,sans-serif]";
const FONT_MONO =
  "font-[family-name:var(--font-geist-mono),ui-monospace,SFMono-Regular,Menlo,monospace]";

const TYPE_DISPLAY =
  "text-[40px] leading-[44px] tracking-[-0.06em] font-normal sm:text-[56px] sm:leading-[56px] md:text-[64px] md:leading-[64px] lg:text-[80px] lg:leading-[80px] xl:text-[96px] xl:leading-[96px] xl:tracking-[-0.07em]";
const TYPE_HEADING_24 =
  "text-[20px] leading-[28px] tracking-[-0.04em] font-semibold sm:text-[24px] sm:leading-[32px]";
const TYPE_HEADING_20 =
  "text-[18px] leading-[24px] tracking-[-0.02em] font-semibold sm:text-[20px] sm:leading-[26px]";
const TYPE_LABEL = `${FONT_MONO} text-[12px] leading-[16px] font-normal uppercase`;
const TYPE_NAV = `${FONT_MONO} text-[12px] leading-[20px] tracking-[0.06em] font-normal uppercase sm:text-[13px]`;
const TYPE_CTA = `${FONT_MONO} text-[11px] leading-[20px] tracking-[0.04em] font-medium uppercase sm:text-[14px] sm:tracking-[0.06em]`;

/* ── Colour ───────────────────────────────────────────────────────────────
 * Literal oklch, light then dark. Two eleven-step semantic ramps reduced to
 * the roles this block actually uses. Blue is a budget: it appears only on
 * primary action, on interactive notches, and on data that has resolved. */

/* surfaces */
const BG_CANVAS = "bg-white dark:bg-black";
const BG_PANEL =
  "bg-[oklch(0.985_0.002_247.839)] dark:bg-[oklch(0.13_0.028_261.692)]";
const BG_CARD =
  "bg-[oklch(0.967_0.003_264.542)] dark:bg-[oklch(0.21_0.034_264.665)]";
const BG_BRAND =
  "bg-[oklch(0.488_0.243_264.376)] dark:bg-[oklch(0.546_0.245_262.881)]";
const BG_BRAND_DEEP =
  "bg-[oklch(0.424_0.199_265.638)] dark:bg-[oklch(0.623_0.214_259.815)]";
const BG_BRAND_TILE =
  "bg-[oklch(0.932_0.032_255.585)] dark:bg-[oklch(0.379_0.146_265.522)]";
const BG_INK =
  "bg-[oklch(0.13_0.028_261.692)] dark:bg-[oklch(0.967_0.003_264.542)]";
const BG_INERT =
  "bg-[oklch(0.872_0.01_258.338)] dark:bg-[oklch(0.373_0.034_259.733)]";

/* text */
const INK =
  "text-[oklch(0.13_0.028_261.692)] dark:text-[oklch(0.967_0.003_264.542)]";
const INK_MUTED =
  "text-[oklch(0.21_0.034_264.665)] dark:text-[oklch(0.707_0.022_261.325)]";
const INK_BRAND =
  "text-[oklch(0.379_0.146_265.522)] dark:text-[oklch(0.707_0.165_254.624)]";
const BRAND =
  "text-[oklch(0.488_0.243_264.376)] dark:text-[oklch(0.546_0.245_262.881)]";
const ON_BRAND = "text-white";

/* interaction */
const HOVER_BRAND =
  "hover:bg-[oklch(0.424_0.199_265.638)] dark:hover:bg-[oklch(0.623_0.214_259.815)]";
const HOVER_SURFACE =
  "hover:bg-[oklch(0.928_0.006_264.531)] dark:hover:bg-[oklch(0.278_0.033_256.848)]";
const HOVER_CARD =
  "hover:bg-[oklch(0.967_0.003_264.542)] dark:hover:bg-[oklch(0.21_0.034_264.665)]";
const ACTIVE_SURFACE =
  "active:bg-[oklch(0.872_0.01_258.338)] dark:active:bg-[oklch(0.373_0.034_259.733)]";
const HOVER_INK =
  "hover:text-[oklch(0.13_0.028_261.692)] dark:hover:text-[oklch(0.967_0.003_264.542)]";

/* focus rings — offset must clear the notch brackets (rest 6px + weight 1.5px) */
const RING_BRAND =
  "focus-visible:outline-2 focus-visible:outline-offset-[10px] focus-visible:outline-[oklch(0.546_0.245_262.881)] dark:focus-visible:outline-[oklch(0.623_0.214_259.815)]";
const RING_GRAY =
  "focus-visible:outline-2 focus-visible:outline-offset-[10px] focus-visible:outline-[oklch(0.446_0.03_256.802)] dark:focus-visible:outline-[oklch(0.551_0.027_264.364)]";
const RING_CHIP =
  "focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-[oklch(0.546_0.245_262.881)] dark:focus-visible:outline-[oklch(0.623_0.214_259.815)]";

/* notch and hatch colours, set per call site as arbitrary properties */
const NOTCH_GRAY =
  "[--akta-notch-color:oklch(0.707_0.022_261.325)] dark:[--akta-notch-color:oklch(0.373_0.034_259.733)]";
const HATCH_GRAY =
  "[--akta-hatch-color:oklch(0.928_0.006_264.531)] dark:[--akta-hatch-color:oklch(0.278_0.033_256.848)]";

/* ── Depth and separators ─────────────────────────────────────────────────
 * A border used for depth is a layered box-shadow; a separator is a
 * single-edge inset shadow. Both are transparent, so they survive on any
 * background — including a photographic plate — and neither costs layout
 * space. Dark mode collapses the layered stacks to one white ring, because
 * layered depth is invisible on a dark ground.
 *
 * The `lg:` and `sm:` prefixed rules are separate constants: a variant has to
 * be part of the class name, so it cannot be added to a shared string. */
const SHADOW_BORDER =
  "shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_1px_2px_-1px_rgba(0,0,0,0.06),0_2px_4px_0_rgba(0,0,0,0.04)] dark:shadow-[0_0_0_1px_rgba(255,255,255,0.08)]";
const SHADOW_PANEL =
  "shadow-[0_0_0_1px_rgba(0,0,0,0.06),0_8px_16px_-6px_rgba(0,0,0,0.08),0_24px_48px_-12px_rgba(0,0,0,0.1)] dark:shadow-[0_0_0_1px_rgba(255,255,255,0.08),0_24px_48px_-12px_rgba(0,0,0,0.6)]";
/* color-mix(in oklab, C 45%, transparent) is just C at 45% alpha */
const RING_TILE =
  "shadow-[0_0_0_1px_oklch(0.623_0.214_259.815/0.45)] dark:shadow-[0_0_0_1px_oklch(0.546_0.245_262.881/0.6)]";

const RULE_T =
  "shadow-[inset_0_1px_0_0_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.1)]";
const RULE_B =
  "shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.08)] dark:shadow-[inset_0_-1px_0_0_rgba(255,255,255,0.1)]";
const RULE_R =
  "shadow-[inset_-1px_0_0_0_rgba(0,0,0,0.08)] dark:shadow-[inset_-1px_0_0_0_rgba(255,255,255,0.1)]";
const RULE_Y =
  "shadow-[inset_0_1px_0_0_rgba(0,0,0,0.08),inset_0_-1px_0_0_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.1),inset_0_-1px_0_0_rgba(255,255,255,0.1)]";
/* right + bottom in one value, so a wrapping grid separates at any column count */
const RULE_CELL =
  "shadow-[inset_-1px_0_0_0_rgba(0,0,0,0.08),inset_0_-1px_0_0_rgba(0,0,0,0.08)] dark:shadow-[inset_-1px_0_0_0_rgba(255,255,255,0.1),inset_0_-1px_0_0_rgba(255,255,255,0.1)]";

const RULE_X_LG =
  "lg:shadow-[inset_1px_0_0_0_rgba(0,0,0,0.08),inset_-1px_0_0_0_rgba(0,0,0,0.08)] dark:lg:shadow-[inset_1px_0_0_0_rgba(255,255,255,0.1),inset_-1px_0_0_0_rgba(255,255,255,0.1)]";
const RULE_L_LG =
  "lg:shadow-[inset_1px_0_0_0_rgba(0,0,0,0.08)] dark:lg:shadow-[inset_1px_0_0_0_rgba(255,255,255,0.1)]";
const RULE_R_LG =
  "lg:shadow-[inset_-1px_0_0_0_rgba(0,0,0,0.08)] dark:lg:shadow-[inset_-1px_0_0_0_rgba(255,255,255,0.1)]";
const RULE_L_SM =
  "sm:shadow-[inset_1px_0_0_0_rgba(0,0,0,0.08)] dark:sm:shadow-[inset_1px_0_0_0_rgba(255,255,255,0.1)]";

/* ── Layout ───────────────────────────────────────────────────────────────
 * The 1440 grid. A band whose rules must cross the whole viewport is a
 * sibling of this container, never a child. */
const GRID = "mx-auto w-full max-w-[90rem]";

/* ── Content ──────────────────────────────────────────────────────────── */

type Mark = { label: string; className: string };

const NAV = [
  { label: "Data", caret: true },
  { label: "Benchmarks" },
  { label: "Pricing" },
  { label: "Resources", caret: true },
  { label: "API Docs", external: true },
];

/* Customer wordmarks, typeset rather than dropped in as logo SVGs — swap each
 * for the real mark when the assets land. They sit on the system's own heading
 * roles, so the block ships no third font family and the wall stays coherent
 * even where Geist never loaded. */
const LOGOS: Mark[] = [
  { label: "KPMG", className: cn(TYPE_HEADING_24, "tracking-tight") },
  { label: "Adobe", className: TYPE_HEADING_24 },
  { label: "JLL", className: cn(TYPE_HEADING_24, "tracking-[0.08em]") },
  { label: "Chicago Booth", className: TYPE_HEADING_20 },
  { label: "BabyAGI", className: TYPE_HEADING_20 },
];

const TAGLINE = [
  "Pay-as-you-go",
  "Built for AI agents",
  "Universal entity resolution",
];

/* One resolved company, shown as a datasheet rather than as JSON. */
const RECORD: [string, string][] = [
  ["ENTITY", "ACME_CORP"],
  ["SECTOR", "SaaS · Fintech"],
  ["HEADCOUNT", "−12%"],
  ["LAST SIGNAL", "4m ago"],
  ["SOURCES", "4"],
];

/* The noise field. Deterministic rather than Math.random() so the server and
 * the client render byte-identical markup — a random field hydrates with a
 * mismatch warning and a visible repaint. 197 of 1,000 articles survive the
 * filter, so ~19.7% of ticks are signal. */
const TICK_COUNT = 168;

function hash(i: number) {
  const x = Math.sin(i * 12.9898 + 78.233) * 43758.5453;
  return x - Math.floor(x);
}

const TICKS = Array.from({ length: TICK_COUNT }, (_, i) => {
  const r = hash(i);
  return { height: 22 + hash(i + 991) * 78, signal: r > 0.803 };
});

/* ── Pieces ───────────────────────────────────────────────────────────── */

/* A button label that re-states itself on hover: the visible face rolls up and
 * an identical copy arrives from below. The duplicate is aria-hidden so the
 * accessible name stays single. */
function RollLabel({ children }: { children: React.ReactNode }) {
  return (
    <span className="akta-roll whitespace-nowrap">
      <span className="akta-roll-face">{children}</span>
      <span className="akta-roll-face akta-roll-face-next" aria-hidden="true">
        {children}
      </span>
    </span>
  );
}

/* One logo-wall cell, cycling through the full set of marks. `offset` staggers
 * both the starting mark and the interval so the six cells never flip in
 * lockstep. Gated on in-view — an interval firing against an off-screen
 * element is pure battery cost. */
function LogoFlip({ marks, offset }: { marks: Mark[]; offset: number }) {
  const ref = useRef<HTMLSpanElement>(null);
  const inView = useInView(ref, { amount: 0.5 });
  const reduced = useReducedMotion();
  const [n, setN] = useState(offset);

  useEffect(() => {
    if (!inView || reduced) return;
    const id = setInterval(() => setN((v) => v + 1), 2600 + offset * 180);
    return () => clearInterval(id);
  }, [inView, reduced, offset]);

  const mark = marks[n % marks.length];

  return (
    <span
      ref={ref}
      aria-hidden="true"
      className="relative flex h-9 w-full items-center justify-center overflow-hidden"
    >
      <AnimatePresence initial={false}>
        <motion.span
          key={n}
          initial={{ y: "110%" }}
          animate={{ y: "0%" }}
          exit={{ y: "-110%" }}
          transition={{ type: "spring", duration: 0.5, bounce: 0 }}
          className={cn(
            "absolute inset-0 flex items-center justify-center",
            mark.className,
          )}
        >
          {mark.label}
        </motion.span>
      </AnimatePresence>
    </span>
  );
}

/* Reduced-motion as an external store rather than an effect, so the phase below
 * can be derived during render instead of written back from a second pass. The
 * server snapshot is `false` — motion is never assumed until the client has
 * actually asked. */
const REDUCE_QUERY = "(prefers-reduced-motion: reduce)";

function subscribeMotion(onChange: () => void) {
  const mql = window.matchMedia(REDUCE_QUERY);
  mql.addEventListener("change", onChange);
  return () => mql.removeEventListener("change", onChange);
}

const motionAllowed = () => !window.matchMedia(REDUCE_QUERY).matches;
const motionAllowedOnServer = () => false;

/* The panel's noise field. Resting state is the *filtered* field, so a no-JS or
 * reduced-motion visitor meets the truthful end state rather than a wall of
 * undifferentiated ticks. Only once motion is confirmed allowed does it arm the
 * raw state, then sweep it back when the panel scrolls into view.
 *
 * All three phases are derived, never assigned: "rest" until motion is
 * confirmed, "armed" once it is, "run" the first time the panel is in view.
 * `once: true` latches that last step, so the field denoises exactly one time. */
function SignalField() {
  const ref = useRef<HTMLDivElement>(null);
  const inView = useInView(ref, { once: true, amount: 0.4 });
  const canMove = useSyncExternalStore(
    subscribeMotion,
    motionAllowed,
    motionAllowedOnServer,
  );

  const phase = !canMove ? "rest" : inView ? "run" : "armed";

  return (
    <div
      ref={ref}
      data-akta-signal={phase}
      aria-hidden="true"
      className="flex h-32 items-end gap-[3px] overflow-hidden px-4 py-5 sm:h-40 sm:px-5 sm:py-6"
    >
      {TICKS.map((tick, i) => (
        <span
          key={i}
          style={
            {
              height: `${tick.height}%`,
              "--akta-i": i,
            } as React.CSSProperties
          }
          className={cn(
            "akta-tick w-[2px]",
            tick.signal ? BG_BRAND : cn("akta-tick-noise", BG_INK),
          )}
        />
      ))}
    </div>
  );
}

export interface AktaHero01Props {
  /* The photographic plate behind the panel cluster. Purely decorative and
   * painted as a CSS background layer, not an <img>: if the file is absent the
   * layer simply does not paint and the hatch beneath it shows through, so the
   * block never ships a broken image. Pass `null` to force the hatch. */
  plateImage?: string | null;
  className?: string;
}

function AktaHero01({
  plateImage = "/paper-image/AIHero01.png",
  className = "",
}: AktaHero01Props) {
  return (
    <section
      className={cn(
        "relative isolate min-h-screen overflow-hidden",
        FONT_SANS,
        BG_CANVAS,
        INK,
        className,
      )}
    >
      <div className={cn(GRID, "relative", RULE_X_LG)}>
        {/* nav — three cells separated by rules, not by padding */}
        <header className="flex items-stretch justify-between">
          <div
            className={cn(
              "flex items-center px-4 py-4 sm:px-6 sm:py-5 lg:px-10",
              RULE_R_LG,
            )}
          >
            <span className={cn(TYPE_HEADING_20, INK)}>
              akta<span className={INK_BRAND}>.pro</span>
            </span>
          </div>

          <nav className="hidden items-center gap-9 lg:flex">
            {NAV.map((item) => (
              <a
                key={item.label}
                href="#"
                className={cn(
                  "flex items-center gap-1.5",
                  TYPE_NAV,
                  INK_MUTED,
                  cn("transition-colors", HOVER_INK),
                )}
              >
                {item.label}
                {item.caret && (
                  <IconChevronDown className="size-3.5" aria-hidden="true" />
                )}
                {item.external && (
                  <IconArrowUpRight className="size-3.5" aria-hidden="true" />
                )}
              </a>
            ))}
          </nav>

          <div className={cn("flex items-center p-2 sm:p-3", RULE_L_LG)}>
            <a
              href="#"
              className={cn(
                "flex items-center px-3 py-2.5 sm:px-5 sm:py-3",
                TYPE_CTA,
                cn(BG_BRAND, ON_BRAND),
                cn("transition-colors", HOVER_BRAND, RING_BRAND),
                "akta-roll-host akta-notch akta-notch-reveal",
              )}
            >
              <RollLabel>
                <IconCornerDownRight className="size-4" aria-hidden="true" />
                Try for free
              </RollLabel>
            </a>
          </div>
        </header>
      </div>

      {/* announcement — the band is full-bleed so its two rules cross the whole
          viewport past the 1440 grid, while the hatch fill and the corner
          notches stay on the grid. The notches land exactly where the vertical
          rails meet the full-bleed rules, marking the junction instead of
          running the rails through the band. */}
      <div className={cn("relative", RULE_Y)}>
        <div
          className={cn(
            cn(
              GRID,
              "flex items-center justify-center px-4 py-3 sm:px-6 sm:py-5",
            ),
            "akta-hatch akta-notch",
            HATCH_GRAY,
            cn(
              NOTCH_GRAY,
              "[--akta-notch-arm:0px] [--akta-notch-inset:0px] [--akta-notch-weight:1px] lg:[--akta-notch-arm:14px]",
            ),
          )}
        >
          <a
            href="#"
            className={cn(
              "flex items-center gap-2 px-3 py-1.5 sm:gap-3 sm:px-4 sm:py-2",
              BG_PANEL,
              SHADOW_BORDER,
              cn("transition-colors", HOVER_CARD, RING_CHIP),
              "akta-roll-host",
            )}
          >
            <span className={cn(TYPE_NAV, INK)}>
              We&apos;re live on <strong>Product Hunt</strong>
            </span>
            <span
              className={cn(
                "akta-roll akta-roll-diagonal size-7 shrink-0 items-center justify-center",
                BG_BRAND,
                ON_BRAND,
              )}
              aria-hidden="true"
            >
              <span className="akta-roll-face">
                <HugeiconsIcon
                  icon={ArrowUpRight01Icon}
                  size={16}
                  strokeWidth={2}
                />
              </span>
              <span className="akta-roll-face akta-roll-face-next">
                <HugeiconsIcon
                  icon={ArrowUpRight01Icon}
                  size={16}
                  strokeWidth={2}
                />
              </span>
            </span>
          </a>
        </div>
      </div>

      <div className={cn(GRID, "relative", RULE_X_LG)}>
        {/* hero */}
        <div className="flex flex-col items-center gap-12 px-4 py-6 text-center sm:gap-16 sm:px-6 sm:py-16 md:py-6 lg:gap-20 lg:px-10 lg:py-8 xl:gap-24 xl:py-10">
          <div className="w-full">
            <h1
              data-akta-enter="1"
              className={cn(
                "mx-auto mt-6 max-w-md text-balance sm:mt-8 sm:max-w-xl md:max-w-2xl xl:max-w-4xl",
                TYPE_DISPLAY,
                INK,
              )}
            >
              Private company data and signals API
            </h1>

            <div
              data-akta-enter="2"
              className="mt-5 flex flex-wrap items-center justify-center gap-y-1 sm:mt-7"
            >
              {/* Separated by rules, not by a pipe glyph. A "|" is a character:
                  it carries its own font metrics, so it sits on a different
                  line-height from the mono labels either side and throws the
                  row's vertical centring.

                  A wrapped row at every width: 59 characters of mono need about
                  470px, so below sm the phrases reflow onto two lines. The
                  rules only appear from sm, where they all fit on one line — a
                  left rule on an item that has wrapped to the start of a new
                  line reads as a stray tick with nothing to its left. */}
              {TAGLINE.map((item, i) => (
                <span
                  key={item}
                  className={cn(
                    "px-2 py-1 md:px-3 lg:px-4",
                    TYPE_NAV,
                    INK_MUTED,
                    i > 0 && RULE_L_SM,
                  )}
                >
                  {item}
                </span>
              ))}
            </div>

            <div
              data-akta-enter="3"
              className="mt-8 flex flex-nowrap justify-center gap-3 sm:mt-12 sm:gap-6"
            >
              <a
                href="#"
                className={cn(
                  "flex items-center px-3 py-2.5 sm:px-6 sm:py-3.5",
                  TYPE_CTA,
                  cn(BG_BRAND, ON_BRAND),
                  cn("transition-colors", HOVER_BRAND, RING_BRAND),
                  "akta-roll-host akta-notch akta-notch-reveal",
                )}
              >
                <RollLabel>
                  <IconCornerDownRight className="size-4" aria-hidden="true" />
                  Try for free
                </RollLabel>
              </a>
              <a
                href="#"
                className={cn(
                  "flex items-center px-3 py-2.5 sm:px-6 sm:py-3.5",
                  TYPE_CTA,
                  cn(BG_CARD, INK),
                  cn(
                    "transition-colors",
                    HOVER_SURFACE,
                    ACTIVE_SURFACE,
                    RING_GRAY,
                  ),
                  /* grey brackets are the only thing separating this from
                     primary — same shape, same type, same size */
                  cn("akta-roll-host akta-notch akta-notch-reveal", NOTCH_GRAY),
                )}
              >
                <RollLabel>Talk to an engineer</RollLabel>
              </a>
            </div>
          </div>

          {/* panels — the panel sits in flow and sets the stage height; the two
              cards flank it, overlapping its edges */}
          <div className="relative isolate flex w-full max-w-2xl flex-col gap-4 text-left sm:gap-6 md:max-w-4xl lg:block lg:max-w-5xl">
            {/* Background plate — a mat the cluster sits on, bled past the stage
                so it frames the panel rather than hiding behind it. No scrim:
                the panel and both cards are opaque, so the texture only ever
                shows in the margin.

                It bleeds to the full width of the 1440 grid: stage, grid and
                viewport share a centre line, so `left-1/2 -translate-x-1/2`
                plus `w-screen` lands it on the rails at every width. It clips
                inside its own wrapper rather than on the stage, because the
                cards and the panel's brackets sit outside the stage box.

                The bottom bleed stays at 40px while the top grows to 64px. It
                is deliberately asymmetric: above the stage is more of the same
                section, but below it is the logo wall, and the plate must stop
                inside the hero's bottom padding. Bleed past that and the
                photograph lands on the wall's top rule — an 8%-black hairline,
                invisible against a dark image — so the wall reads as having no
                separator at all. */}
            <div
              className="akta-plate absolute -top-10 -bottom-10 left-1/2 -z-10 w-screen -translate-x-1/2 overflow-hidden sm:-top-16"
              style={
                plateImage
                  ? ({
                      "--akta-plate-image": `url(${plateImage})`,
                    } as React.CSSProperties)
                  : undefined
              }
              aria-hidden="true"
            />

            <div
              data-akta-enter="5"
              className={cn(
                "z-20 order-2 w-full p-4 lg:absolute lg:-top-6 lg:left-0 lg:order-none lg:w-56",
                BG_CARD,
                SHADOW_PANEL,
              )}
            >
              <div className="flex items-center gap-2.5">
                <span
                  className={cn(
                    "flex size-7 items-center justify-center",
                    BG_BRAND_TILE,
                    RING_TILE,
                  )}
                >
                  <IconChartBar
                    className={cn("size-4", INK_BRAND)}
                    aria-hidden="true"
                  />
                </span>
                <span className={cn(TYPE_LABEL, INK)}>
                  Sentiment
                  <br />
                  analysis
                </span>
              </div>
              <div
                className="mt-4 flex h-10 items-end gap-2"
                aria-hidden="true"
              >
                {[40, 65, 100, 55, 30, 70].map((h, i) => (
                  <span
                    key={i}
                    style={{ height: `${h}%` }}
                    className={cn(
                      "w-1.5",
                      h === 100 ? BG_BRAND_DEEP : BG_INERT,
                    )}
                  />
                ))}
              </div>
              <p className={cn("mt-3", TYPE_LABEL, INK_BRAND)}>
                Positive trend detected
              </p>
            </div>

            <div
              data-akta-enter="4"
              className={cn(
                "relative z-10 order-1 mx-auto w-full lg:order-none lg:max-w-2xl",
                BG_PANEL,
                SHADOW_PANEL,
                "akta-notch akta-notch-diagonal [--akta-notch-arm:22px] [--akta-notch-inset:10px]",
              )}
            >
              <div
                className={cn(
                  "flex items-center justify-between px-3 py-2 sm:px-4 sm:py-2.5",
                  BG_CARD,
                  RULE_B,
                )}
              >
                <span
                  className={cn("flex items-center gap-2", TYPE_LABEL, INK)}
                >
                  <IconFilter
                    className={cn("size-4", BRAND)}
                    aria-hidden="true"
                  />
                  Signal extraction
                </span>
                <span className={cn(TYPE_LABEL, INK_MUTED)}>Last 24h</span>
              </div>

              <SignalField />

              <p className="sr-only">
                Of 1,000 articles ingested in the last 24 hours, 197 were
                retained as signals — 80 percent were filtered out as noise.
              </p>

              <div className={cn("grid grid-cols-2", RULE_T)}>
                <div className={cn("px-4 py-3 sm:px-5 sm:py-4", RULE_R)}>
                  <p className={cn(TYPE_LABEL, INK_MUTED)}>Articles ingested</p>
                  <p className={cn("mt-1 tabular-nums", TYPE_HEADING_24, INK)}>
                    1,000
                  </p>
                </div>
                <div className="px-4 py-3 sm:px-5 sm:py-4">
                  <p className={cn(TYPE_LABEL, INK_BRAND)}>Signals retained</p>
                  <p
                    className={cn("mt-1 tabular-nums", TYPE_HEADING_24, BRAND)}
                  >
                    197
                  </p>
                </div>
              </div>
            </div>

            <div
              data-akta-enter="6"
              className={cn(
                "z-20 order-3 w-full p-4 lg:absolute lg:right-0 lg:-bottom-6 lg:order-none lg:w-64",
                BG_CARD,
                SHADOW_PANEL,
              )}
            >
              <div className="flex items-center gap-2.5">
                <span
                  className={cn(
                    "flex size-7 items-center justify-center",
                    BG_BRAND_TILE,
                    RING_TILE,
                  )}
                >
                  <IconFileDescription
                    className={cn("size-4", INK_BRAND)}
                    aria-hidden="true"
                  />
                </span>
                <span className={cn(TYPE_LABEL, INK)}>Canonical record</span>
              </div>
              <dl className={cn("mt-4 space-y-1.5", TYPE_LABEL, INK_MUTED)}>
                {RECORD.map(([field, value]) => (
                  <div key={field} className="flex justify-between gap-4">
                    <dt>{field}</dt>
                    <dd className={cn("tabular-nums", INK)}>{value}</dd>
                  </div>
                ))}
              </dl>
              <p className={cn("mt-3 pt-3", TYPE_LABEL, INK_BRAND, RULE_T)}>
                + 65 more data points
              </p>
            </div>
          </div>
        </div>
      </div>

      {/* logo wall — the row is full-bleed so its rules cross the viewport,
          while the cells sit on the 1440 grid. Every cell carries a flush
          notch, so each divider is bracketed top and bottom where it meets the
          row's rules. The wall is opaque and raised so it cleanly clips the
          plate bleeding down from the stage above. */}
      <div className={cn("relative z-10 px-2 sm:px-0", BG_PANEL, RULE_T)}>
        <div
          className={cn(GRID, "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6")}
        >
          <div
            className={cn(
              "flex flex-col justify-center gap-1 px-4 py-6 text-left sm:px-6 sm:py-8",
              RULE_CELL,
              cn(
                "akta-notch",
                NOTCH_GRAY,
                "[--akta-notch-arm:8px] [--akta-notch-inset:0px] [--akta-notch-weight:1px] lg:[--akta-notch-arm:10px]",
              ),
            )}
          >
            <span className={cn(TYPE_LABEL, INK_MUTED)}>
              Trusted by{" "}
              <span className={cn("tabular-nums", BRAND)}>5,000+</span> top
              companies
            </span>
          </div>

          {LOGOS.map((mark, i) => (
            <div
              key={mark.label}
              className={cn(
                "flex items-center justify-center gap-2 px-3 py-6 sm:px-4 sm:py-8",
                INK,
                RULE_CELL,
                cn(
                  "akta-notch",
                  NOTCH_GRAY,
                  "[--akta-notch-arm:8px] [--akta-notch-inset:0px] [--akta-notch-weight:1px] lg:[--akta-notch-arm:10px]",
                ),
              )}
            >
              <LogoFlip marks={LOGOS} offset={i} />
            </div>
          ))}
        </div>

        {/* The wall's cells flip through the marks and are aria-hidden, so the
            customer names are stated once here for assistive tech. */}
        <p className="sr-only">
          Trusted by 5,000+ top companies, including{" "}
          {LOGOS.map((m) => m.label).join(", ")}.
        </p>
      </div>

      {/* hatch seam closing the wall */}
      <div className={cn("relative z-10", BG_PANEL, RULE_Y)}>
        <div className={cn(GRID, "akta-hatch h-6 sm:h-9", HATCH_GRAY)} />
      </div>
    </section>
  );
}

export default AktaHero01;
```

### `components/craftui/sections/akta-hero-01/design.md`

````md
# Akta — Style Reference

> technical drawing on white paper

**Theme:** light, with a complete dark ramp under the `.dark` class

Akta is a drafting system, not a card system. The canvas is pure white, every surface is a **square-cornered plane** separated by **hairline rules** rather than boxes, and the recurring ornament is a **corner notch** — an L-shaped crop mark borrowed from print registration. There is no border-radius anywhere, no gradient, and no decorative colour: a single blue does all the emphasis work against an eleven-step gray ramp, and it is spent only on action and on data. The UI voice is **uppercase monospace** — labels, nav and buttons are all mono — which is what makes the page read as instrument panel rather than marketing site. Sans is reserved for headlines, figures and body copy.

The structural idea underneath everything: **borders are shadows.** Depth is a layered `box-shadow`; separators are single-edge `inset` shadows. Nothing uses the `border` property except the corner notch itself.

---

## How this ships

**Two files, and one CSS block.** `default.tsx` carries the markup *and every value*; `design.md` is this document. The only thing written into your `globals.css` is the six device classes, which do things a utility cannot express.

There is **no `@theme` block, no `:root` palette, and no reset.** Nothing here can collide with — or be re-skinned by — your stylesheet, and no class can silently do nothing because a token was never installed. Change a value and it changes in one place: the constant at the top of `default.tsx`.

### The tokens live in the component

Every value is a Tailwind arbitrary value with its dark twin, grouped into named constants at the top of the file:

```tsx
const BG_BRAND =
  "bg-[oklch(0.488_0.243_264.376)] dark:bg-[oklch(0.546_0.245_262.881)]";
const TYPE_CTA = `${FONT_MONO} text-[11px] leading-[20px] tracking-[0.04em] ` +
  `font-medium uppercase sm:text-[14px] sm:tracking-[0.06em]`;
const RULE_T =
  "shadow-[inset_0_1px_0_0_rgba(0,0,0,0.08)] dark:shadow-[inset_0_1px_0_0_rgba(255,255,255,0.1)]";
```

Dark mode is carried by a `dark:` twin on every colour rather than by swapping one variable. That is the cost of owning the values outright — and the reason they are constants rather than repeated at each of the 58 call sites.

### `cn()` groups every className

Classes are assembled in a fixed argument order, so a given concern is always in the same place:

```tsx
cn( layout , type , colour , state , device )
```

```tsx
<a className={cn(
  "flex items-center px-3 py-2.5 sm:px-6 sm:py-3.5",     // layout
  TYPE_CTA,                                              // type
  cn(BG_BRAND, ON_BRAND),                                // colour
  cn("transition-colors", HOVER_BRAND, RING_BRAND),      // state
  "akta-roll-host akta-notch akta-notch-reveal",         // device
)}>
```

`cn` is `twMerge(clsx(...))`. It correctly separates `text-[40px]` (size) from `text-[oklch(…)]` (colour) — but it **will** collapse two `shadow-*` classes, because `box-shadow` is one property. Never put a rule and a depth shadow on the same element; put one on a wrapper.

### What stays as CSS, and why

Six things a utility genuinely cannot express. They live in `@layer components`, which sits *before* `utilities` in the cascade, so a utility at any call site still wins over them.

| Device | Why it can't be a utility |
|---|---|
| `.akta-notch` | Pseudo-element carrying a four-layer mask |
| `.akta-hatch` | Repeating gradient with four tunable properties |
| `.akta-roll` | Host + two faces + hover states across three elements |
| `.akta-tick` | Per-index stagger: `calc(var(--akta-i) * 3ms)` |
| `.akta-plate` | Two-layer background with graceful-degradation fallback |
| `[data-akta-enter]` / `[data-akta-reveal]` | `@keyframes` has no utility form |

Two of them carry colour, so they have a `.dark` override in the block: `.dark .akta-notch` and `.dark .akta-plate`. Everything else in there is geometry.

**The rule when you extend the system: values are utilities, mechanism is CSS.** A new colour or size becomes a constant in the component. A new pseudo-element or keyframe goes in `@layer components`.

### Assumptions

| Assumption | If it isn't true |
|---|---|
| **Tailwind v4** | Arbitrary values, `dark:` stacking and `@layer components` ordering are v4-shaped. |
| Preflight enabled (default) | Element margins and link colours return; the block ships no reset of its own. |
| Dark mode toggles a **`.dark`** class | The dark ramp never activates. For `[data-theme="dark"]`, duplicate the `.dark { … }` block. |
| Geist loaded as `--font-geist-sans` / `--font-geist-mono` | **Optional.** `FONT_SANS` / `FONT_MONO` fall back to `ui-sans-serif` / `ui-monospace`. |
| `public/paper-image/AIHero01.png` exists | **Optional.** The plate is a CSS background layer; a missing file falls through to the hatch. |

---

## Tokens — Colors

Two ramps, eleven semantic roles each, as **literal oklch** rather than references to Tailwind's palette — so the block looks identical in a project that customised its own blue. Each role in use is a named constant in `default.tsx` pairing the light value with its `dark:` twin.

**Always use the constant, never a bare literal at a call site.** A raw `bg-[oklch(…)]` written inline has no dark twin, so it will not switch themes — that is the one failure mode this arrangement still allows.

### Brand — blue

| Name | Light | Dark | Constant | Where | Role | In block |
|------|-------|------|-------|---------|------|------|
| Brand BG Subtle | `#eff6ff` | `#162456` | — | — | Tinted page areas, callout grounds | ➕ add |
| Brand UI | `#dbeafe` | `#1c398e` | `BG_BRAND_TILE` | in `default.tsx` | Icon tile fills, chip fills | ✅ |
| Brand UI Hover | `#bedbff` | `#193cb8` | — | — | Hover on a tinted chip | ➕ add |
| Brand UI Active | `#8ec5ff` | `#1447e6` | — | — | Pressed tinted chip | ➕ add |
| Brand Border Subtle | `#51a2ff` | `#1447e6` | — | — | Faint brand edge | ➕ add |
| Brand Border | `#2b7fff` | `#155dfc` | `RING_TILE (45% alpha)` | in `default.tsx` | Source for `RING_TILE` | ✅ |
| Brand Border Hover | `#155dfc` | `#2b7fff` | `RING_BRAND` | in `default.tsx` | **Focus rings on brand controls** | ✅ |
| Brand Solid | `#1447e6` | `#155dfc` | `BG_BRAND / BRAND` | in `default.tsx` | **Primary buttons, interactive notches, signal data** | ✅ |
| Brand Solid Hover | `#193cb8` | `#2b7fff` | `HOVER_BRAND` | in `default.tsx` | Primary button hover | ✅ |
| Brand Text Low | `#1c398e` | `#51a2ff` | `INK_BRAND` | in `default.tsx` | Brand-tinted labels, icons on tinted fills | ✅ |
| Brand Text High | `#162456` | `#dbeafe` | — | — | Text on brand chips | ➕ add |
| Brand On Solid | `#ffffff` | `#ffffff` | `ON_BRAND` | in `default.tsx` | Text on `BG_BRAND` | ✅ |

### Gray

| Name | Light | Dark | Constant | Where | Role | In block |
|------|-------|------|-------|---------|------|------|
| Gray BG Subtle | `#f9fafb` | `#030712` | `BG_PANEL` | in `default.tsx` | Panel surfaces, chips lifted off a band | ✅ |
| Gray UI | `#f3f4f6` | `#101828` | `BG_CARD / HOVER_CARD` | in `default.tsx` | Card fills, panel title bars, secondary buttons | ✅ |
| Gray UI Hover | `#e5e7eb` | `#1e2939` | `HOVER_SURFACE / HATCH_GRAY` | in `default.tsx` | Secondary button hover, hatch stroke | ✅ |
| Gray UI Active | `#d1d5dc` | `#364153` | `ACTIVE_SURFACE / BG_INERT` | in `default.tsx` | Pressed state, inert chart bars | ✅ |
| Gray Border Subtle | `#99a1af` | `#364153` | `NOTCH_GRAY` | in `default.tsx` | **Structural notches** | ✅ |
| Gray Border | `#6a7282` | `#4a5565` | — | — | Inert dots, low-contrast marks *(provided, unexercised)* | ➕ add |
| Gray Border Hover | `#4a5565` | `#6a7282` | `RING_GRAY` | in `default.tsx` | Focus rings on neutral controls | ✅ |
| Gray Solid | `#364153` | `#4a5565` | — | — | — | ➕ add |
| Gray Solid Hover | `#1e2939` | `#6a7282` | — | — | — | ➕ add |
| Gray Text Low | `#101828` | `#99a1af` | `INK_MUTED` | in `default.tsx` | Secondary text, mono labels | ✅ |
| Gray Text High | `#030712` | `#f3f4f6` | `INK / BG_INK` | in `default.tsx` | Primary text, headlines, noise ticks | ✅ |
| Gray On Solid | `#ffffff` | `#ffffff` | — | — | Text on a solid gray | ➕ add |
| **Canvas** | `#ffffff` | `#000000` | `BG_CANVAS` | in `default.tsx` | **Page ground** | ✅ |

Canvas is deliberately one step past Gray BG Subtle, so panels separate from the page without a border.

### The blue budget

Blue is scarce on purpose. Six permitted uses, and no others:

1. Primary button fills
2. Corner notches on **interactive** elements and the hero panel
3. Signal data — the retained ticks, the `197` figure
4. Figures that are the point of their sentence — the `5,000+`, the `.pro` in the wordmark
5. Icons sitting on a `BG_BRAND_TILE` tile
6. A bare icon or status line labelling *data as resolved* — the filter glyph, "Positive trend detected", "+ 65 more data points"

**Structural notches are `NOTCH_GRAY`**, never blue — logo wall cells, band junctions, the secondary button. If blue appears more than a handful of times per viewport, the primary action stops reading as primary.

---

## Tokens — Typography

### Geist Sans — headlines, figures, body copy · `FONT_SANS`

- **Substitute:** `ui-sans-serif, system-ui, sans-serif` (automatic fallback — Geist is optional)
- **Weights:** 400, 500, 600
- **Sizes:** 13, 15, 16, 18, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96
- **Letter spacing:** −0.06em at display, easing to −0.01em at copy. Tracking is expressed in `em`, so one value is correct at every step of a responsive ramp — this is the face's own rule (roughly −6% of size at display sizes) rather than a per-breakpoint guess.
- **Weight falls as size rises:** 600 at 20px, 500 at 32px, 400 at 48px and above. Large text needs less weight to carry.

### Geist Mono — all UI chrome · `FONT_MONO`

- **Substitute:** `ui-monospace, SFMono-Regular, Menlo, monospace`
- **Weights:** 400, 500
- **Sizes:** 11, 12, 13, 14
- **Letter spacing:** positive — `0.04em`–`0.06em`. Uppercase mono at these sizes needs air, and the tracking is what stops labels reading as compressed.
- **Always uppercase in use.** `uppercase` is baked into the three mono constants, so it never has to be remembered at a call site.

### Type Scale

Ten roles. Each is **one constant carrying size, line-height, letter-spacing, weight and every responsive step** — so a heading is a single `TYPE_*` argument to `cn()`, not five classes.

Six are in the block. The other four are the section-heading and body-copy roles a later section will need; their numbers are here at full precision, ready to become constants.

| Role | Step | Size | Line Height | Tracking | Weight | Constant |
|------|------|------|-------------|----------|--------|---------|
| **display** | base | 40px | 44px (1.1) | −0.06em | 400 | `TYPE_DISPLAY` |
|  | sm | 56px | 56px (1.0) | −0.06em | 400 |  |
|  | md | 64px | 64px (1.0) | −0.06em | 400 |  |
|  | lg | 80px | 80px (1.0) | −0.06em | 400 |  |
|  | xl | 96px | 96px (1.0) | −0.07em | 400 |  |
| **heading-48** ➕ | base | 32px | 36px (1.12) | −0.06em | 400 | ➕ add |
|  | sm | 40px | 44px (1.1) | −0.06em | 400 |  |
|  | lg | 48px | 52px (1.08) | −0.06em | 400 |  |
| **heading-32** ➕ | base | 24px | 30px (1.25) | −0.05em | 500 | ➕ add |
|  | sm | 28px | 34px (1.21) | −0.05em | 500 |  |
|  | lg | 32px | 38px (1.19) | −0.05em | 500 |  |
| **heading-24** | base | 20px | 28px (1.4) | −0.04em | 600 | `TYPE_HEADING_24` |
|  | sm | 24px | 32px (1.33) | −0.04em | 600 |  |
| **heading-20** | base | 18px | 24px (1.33) | −0.02em | 600 | `TYPE_HEADING_20` |
|  | sm | 20px | 26px (1.3) | −0.02em | 600 |  |
| **copy-16** ➕ | base | 15px | 24px (1.6) | −0.01em | 400 | ➕ add |
|  | sm | 16px | 26px (1.625) | −0.01em | 400 |  |
| **copy-14** ➕ | base | 13px | 20px (1.54) | −0.01em | 400 | ➕ add |
|  | sm | 14px | 22px (1.57) | −0.01em | 400 |  |
| **label-12-mono** | base | 12px | 16px (1.33) | 0 | 400 | `TYPE_LABEL` |
| **nav** | base | 12px | 20px (1.667) | 0.06em | 400 | `TYPE_NAV` |
|  | sm | 13px | 20px (1.538) | 0.06em | 400 |  |
| **cta** | base | 11px | 20px (1.82) | 0.04em | 500 | `TYPE_CTA` |
|  | sm | 14px | 20px (1.43) | 0.06em | 500 |  |

### When to use which

| Role | Use for | Write |
|---|---|---|
| **display** | Hero `h1`, once per page | `cn(TYPE_DISPLAY)` |
| **heading-48** ➕ | Section `h2` | add a `TYPE_*` constant from the table above |
| **heading-32** ➕ | Sub-section `h3` | add a `TYPE_*` constant from the table above |
| **heading-24** | Panel figures, stat values | `cn(TYPE_HEADING_24, "tabular-nums")` |
| **heading-20** | Wordmark, card titles | `cn(TYPE_HEADING_20)` |
| **copy-16** ➕ | Body paragraphs | add a `TYPE_*` constant from the table above |
| **copy-14** ➕ | Dense body, captions under figures | add a `TYPE_*` constant from the table above |
| **label-12-mono** | Every UI label, eyebrows, data keys | `cn(TYPE_LABEL)` |
| **nav** | Nav links, microcopy, taglines | `cn(TYPE_NAV)` |
| **cta** | All buttons | `cn(TYPE_CTA)` |

**Family is inherited.** `FONT_SANS` sits once on the section root; the three mono constants carry `FONT_MONO` themselves.

**Roles are overridable.** `cn()` is `twMerge`, so a later argument wins: `cn(TYPE_HEADING_24, "tracking-tight")` keeps the size and replaces the tracking. That is how the logo wall gives KPMG and JLL their own letterforms.

**One role per element.** `cn(TYPE_NAV, TYPE_LABEL)` does not compose — both set every property, and twMerge keeps only the later, so the result is whichever you wrote second.

---

## Tokens — Spacing & Shapes

**Base unit:** 4px · **Density:** comfortable at section scale, compact at control scale

### Spacing Scale

| Name | Value | Use |
|------|-------|-----|
| 1 | 4px | Icon-to-label gap in tight rows |
| 2 | 8px | Chip padding, gap between adjacent controls |
| 2.5–3 | 10–12px | Button horizontal padding (mobile), card inner gap |
| 4 | 16px | Card padding, gutter (mobile), panel body padding |
| 5 | 20px | Panel cell padding |
| 6 | 24px | Gutter (tablet), logo cell padding |
| 8–10 | 32–40px | Gutter (desktop), band padding |
| 12–28 | 48–112px | Section rhythm |

### Border Radius

| Element | Value |
|---------|-------|
| **Everything** | **0** |

No exceptions. Square corners plus corner notches *are* the identity — a rounded corner would take the ornament's job.

### Shadows

| Name | Light | Dark | Constant | Shipped |
|------|-------|------|-------|---------|
| Border | `0 0 0 1px rgb(0 0 0/.06), 0 1px 2px -1px rgb(0 0 0/.06), 0 2px 4px rgb(0 0 0/.04)` | `0 0 0 1px rgb(255 255 255/.08)` | `SHADOW_BORDER` | ✅ |
| Border Hover | same at `.08/.08/.06` | `0 0 0 1px rgb(255 255 255/.13)` | ➕ add | ➕ add |
| Panel | `0 0 0 1px rgb(0 0 0/.06), 0 8px 16px -6px rgb(0 0 0/.08), 0 24px 48px -12px rgb(0 0 0/.1)` | `0 0 0 1px rgb(255 255 255/.08), 0 24px 48px -12px rgb(0 0 0/.6)` | `SHADOW_PANEL` | ✅ |
| Ring Brand | `0 0 0 1px` brand-border @ 45% | @ 60% | `RING_TILE` | ✅ |

### Rules — separators

| Name | Value (light) | Constant |
|------|---------------|-------|---------|
| Top | `inset 0 1px 0 0 rgb(0 0 0/.08)` | `RULE_T` | ✅ |
| Bottom | `inset 0 -1px 0 0 rgb(0 0 0/.08)` | `RULE_B` | ✅ |
| Left / Right | `inset ±1px 0 0 0` | `RULE_R`, `RULE_L_LG`, `RULE_R_LG`, `RULE_L_SM` | ➕ add |
| X / Y | both sides / both ends | `RULE_X_LG`, `RULE_Y` | ➕ add |
| Cell | right **+** bottom in one value | `RULE_CELL` | ✅ |

Dark mode uses `rgb(255 255 255/.1)` for all rules.

`RULE_CELL` is what a wrapping grid uses — every cell carries it, so rows and columns separate at any column count and the outer edges land on the container rail where they coincide. **Do not** use `RULE_R` + `last:shadow-none`; that breaks the moment the grid reflows to a different column count.

Two consequences of shadows-as-borders:

- **Inset shadows paint below child backgrounds.** A full-width child with an opaque background covers a parent's rails — which is why the logo wall carries `BG_PANEL z-10`.
- **Separators occupy no space.** Converting a `border` to a rule shifts adjacent content by 1px per edge.

### Easing

| Name | Value | Where |
|------|-------|-------|
| Akta | `cubic-bezier(0.16, 1, 0.3, 1)` (expo-out) | written inline in the `@layer components` transitions |

One curve for the whole system. Springs use `{ type: "spring", duration: 0.5, bounce: 0 }` — bounce is always `0`.

---

## Layout

| Property | Value | Utility |
|---|---|---|
| Page max-width | 1440px | `GRID` — `mx-auto w-full max-w-[90rem]` |
| Gutters | 16 / 24 / 40px | `px-4 sm:px-6 lg:px-10` |
| Section rhythm | 48 → 112px | `py-12 sm:py-16 md:py-20 lg:py-24 xl:py-28` |
| Stack gap | 48 → 96px | `gap-12 sm:gap-16 lg:gap-20 xl:gap-24` |
| Card padding | 16px | `p-4` |
| Panel cell padding | 16–20px | `px-4 py-3 sm:px-5 sm:py-4` |
| Element gap | 8–12px | `gap-2` / `gap-3` |
| Body measure | 65ch | `max-w-[65ch]` |

**Padding and gaps only ever grow with the viewport** — never step down at a larger breakpoint.

**Widths come from the scale, not from pixels.** `max-w-2xl md:max-w-4xl lg:max-w-5xl` for a stage, `w-56` / `w-64` for cards. No `w-[620px]`.

### Z-index

| Value | Utility | Use |
|---|---|---|
| −1 | `-z-10` | Background plates and textures behind content |
| 10 | `z-10` | Opaque bands that must clip a neighbour's bleed |
| 20 | `z-20` | Cards overlapping a panel |

### Icon sizes

| Size | Utility | Use |
|---|---|---|
| 14px | `size-3.5` | Inline with nav or label text |
| 16px | `size-4` | Inside buttons; inside a tile |
| 28px | `size-7` | The tile itself |

Stroke weight 1.5–2px. Tabler and Hugeicons are the two sets in use.

### Full-bleed bands

A band whose rules must cross the whole viewport is a **sibling of the grid container**, not a child:

```html
<div className={cn("relative", RULE_Y)}>          {/* rules run edge to edge */}
  <div className={cn(GRID, "…")}>                  {/* content stays on grid */}
```

To bleed a *child* out to the rails from inside a padded column, centre it on the viewport: `absolute left-1/2 w-screen -translate-x-1/2`; `.akta-plate` already caps itself at 90rem. Stage, grid and viewport share a centre line, so this lands on the rails at every width. `w-screen` includes the scrollbar gutter — clip it with `overflow-hidden` on an ancestor.

---

## Surfaces

| Level | Name | Light | Dark | Purpose |
|-------|------|-------|------|---------|
| 0 | Canvas | `#ffffff` | `#000000` | Page ground, broadest layer |
| 1 | Panel / Chip | `#f9fafb` | `#030712` | Panel bodies, chips lifted off a hatch band |
| 2 | Card / Title bar | `#f3f4f6` | `#101828` | Flanking cards, panel title bars, secondary buttons |
| 3 | Solid | `#1447e6` | `#155dfc` | Primary buttons, brand squares — the only saturated surface |

The stack runs **light-to-dark in light mode and dark-to-light in dark mode**, but the *relationship* is constant: each level is one step away from its parent, and separation never needs a border. A card on a panel on the canvas is three tones, no lines.

---

## Elevation

- **Panel / flanking card:** `SHADOW_PANEL` — 1px ring + 8px lift + 24px ambient. The only real elevation in the system.
- **Chip / resting surface:** `SHADOW_BORDER` — 1px ring + 1px lift + 2px ambient. Reads as "sitting on" rather than "floating above".
- **Button (filled):** none. Relies on tonal contrast and the notch, not shadow.
- **Icon tile:** `RING_TILE` — a 1px ring in the brand hue at 45% (60% in dark), no offset.
- **Separators:** `RULE_*`. Not elevation — these are hairlines that cost no layout space.

In dark mode the three-layer stacks collapse to a single white ring, because layered depth is invisible on a dark ground.

---

## Signature Devices

The six CSS classes. Everything else is a utility.

### Corner notch — `.akta-notch`

L-shaped crop marks at an element's corners. One pseudo-element draws a **full border**, then a **four-layer mask** keeps only the corners. No wrapper markup, no four extra spans, and the brackets always track the element's real size.

| Property | Default | Purpose |
|---|---|---|
| `--akta-notch-arm` | `12px` | length of each leg of the L |
| `--akta-notch-weight` | `1.5px` | stroke width (`1px` on `.akta-notch-reveal` below sm) |
| `--akta-notch-color` | `BG_BRAND` | stroke colour |
| `--akta-notch-inset` | `6px` | distance outside the box; negative tucks inward |
| `--akta-notch-rest` | `6px` | resting offset for `.akta-notch-reveal` (`4px` below sm) |

Tune with Tailwind arbitrary properties: `[--akta-notch-arm:22px] lg:[--akta-notch-arm:10px]`.

| Variant | Corners |
|---|---|
| `.akta-notch` | all four |
| `.akta-notch-top` | top pair |
| `.akta-notch-bottom` | bottom pair |
| `.akta-notch-diagonal` | top-left + bottom-right — reads as a registration mark rather than a frame |
| `.akta-notch-reveal` | animates flush on hover / focus-visible |

All variants are modifiers — pair them with `.akta-notch`.

**`overflow-hidden` on a notched element shears the brackets off.** They live outside the box. Put clipping on an inner wrapper.

**Flush notches mark junctions.** `[--akta-notch-inset:0px] [--akta-notch-arm:0px] lg:[--akta-notch-arm:10px]` puts brackets exactly on the corners — used where vertical rails meet full-bleed rules, and on every logo wall cell. Arms collapse to zero below `lg` because the rails are `lg:`-only.

### Hatch — `.akta-hatch`

A repeating 45° rule marking a band as structural rather than content-bearing — the drafting paper between sections. Paints `background-image` only, so the element keeps its own background colour.

| Property | Default |
|---|---|
| `--akta-hatch-color` | `currentColor` @ 12% |
| `--akta-hatch-weight` | `1px` |
| `--akta-hatch-gap` | `7px` |
| `--akta-hatch-angle` | `45deg` |

Variants: `.akta-hatch-reverse` (mirrored — pairs across a seam), `.akta-hatch-dense` (4px gap).

Content laid on a hatch needs an **opaque box** behind it or it becomes unreadable.

### Rolling label — `.akta-roll`

A label that re-states itself on hover: the visible face rolls up and out while an identical copy arrives from below. `.akta-roll-host` on the interactive element, `.akta-roll` on the label wrapper.

```html
<a class="akta-roll-host akta-notch akta-notch-reveal">
  <span class="akta-roll">
    <span class="akta-roll-face">Try for free</span>
    <span class="akta-roll-face akta-roll-face-next" aria-hidden="true">Try for free</span>
  </span>
</a>
```

The clip lives on the label wrapper, never the button — the button is a notch and `overflow: hidden` would shear its brackets.

`.akta-roll-diagonal` sends the face out through the top-right corner and brings its replacement from the bottom-left, for arrows that should leave along the direction they point. In that variant **both** faces are absolutely positioned, so a 100% translate is a full box width rather than a glyph width.

The duplicate is always `aria-hidden` so the accessible name stays single.

### Background plate — `.akta-plate`

A photographic mat for a cluster of panels, painted as a **CSS background layer** rather than an `<img>`, with the hatch as a second layer beneath. A missing or 404'd image simply doesn't paint and the hatch shows through, so the plate can never render broken.

```jsx
<div className="akta-plate absolute inset-0 -z-10 overflow-hidden"
     style={{ "--akta-plate-image": "url(/paper-image/AIHero01.png)" }} />
```

Leave `--akta-plate-image` unset for a pure hatch plate. The class caps itself at 1440px, so it can carry `w-screen` for a full-bleed mat.

### Signal ticks — `.akta-tick`

A field of ticks that resolves from noise into signal, demonstrating a filter rather than describing one.

| State | Selector | Effect |
|---|---|---|
| Rest | `.akta-tick-noise` | 16% opacity — the filtered end state |
| Armed | `[data-akta-signal="armed"]` | full opacity — the raw field |
| Run | `[data-akta-signal="run"]` | animates back down, staggered by `--akta-i` |

Resting state is the *filtered* field, so a no-JS or reduced-motion visitor meets the truthful end state rather than a field that never resolves.

### Entrance — `[data-akta-enter]` / `[data-akta-reveal]`

See Motion below.

---

## Components

### Primary Button
**Role:** High-emphasis action (Try for free, Get started)

`cn(BG_BRAND, ON_BRAND)`, no border, **radius 0**, padding 12px 24px (`px-6 py-3.5`), `TYPE_CTA` at its sm step (14px/500 uppercase, 0.06em). Height ≈ 48px. Devices: `akta-roll-host akta-notch akta-notch-reveal`. Hover moves to `HOVER_BRAND`, the brackets pull flush from 6px to 0, and the label rolls. Focus ring at `outline-offset-[10px]` to clear the brackets. No shadow — the notch and the tonal contrast do the work.

### Secondary Button
**Role:** Low-emphasis paired action (Talk to an engineer)

Identical geometry on `BG_CARD` with `NOTCH_GRAY` and `ACTIVE_SURFACE`. **Grey brackets are the only thing separating it from primary** — same shape, same type, same size. Keep it that way.

### Announcement Band
**Role:** Full-width notice above the hero

Full-bleed wrapper with `RULE_Y` so the rules cross the viewport; hatch fill on the grid; flush notches at the four junctions where the rails meet the rules. Inside, a solid `BG_PANEL` chip with `SHADOW_BORDER`, carrying nav-role text plus a `size-7` `BG_BRAND` square with a diagonal-rolling arrow.

### Panel
**Role:** The centrepiece that demonstrates the product

`BG_PANEL SHADOW_PANEL`, `akta-notch akta-notch-diagonal` with `[--akta-notch-arm:22px] [--akta-notch-inset:10px]`, `w-full lg:max-w-2xl`. Title bar in `BG_CARD` with a `RULE_B` baseline, label left and context right, both label-12-mono uppercase. Body carries the graphic. Footer is a two-cell grid split by `RULE_R`, figures in heading-24 with `tabular-nums`.

### Flanking Card
**Role:** Secondary evidence beside the panel

`w-56`–`w-64`, `BG_CARD p-4 SHADOW_PANEL`. **Stacks in flow below `lg`** (`order-2` / `order-3`) and becomes `lg:absolute` overlapping the panel — never hidden, because on mobile it carries content the panel doesn't. Always: `size-7` icon tile (`BG_BRAND_TILE RING_TILE`) beside an uppercase mono title, then content.

### Logo Wall
**Role:** Social proof strip closing the hero

Full-bleed, opaque, `z-10`, `RULE_T`. Grid of `2 / sm:3 / lg:6` cells, each `RULE_CELL` with a flush gray notch, `px-4 py-6 sm:px-6 sm:py-8`. Closed by a hatch seam band. Cells that animate are `aria-hidden` with one `sr-only` sentence naming every customer.

### Data List
**Role:** Canonical record, key-value readout

`<dl>` in label-12-mono, `flex justify-between` rows with `space-y-1.5`. Keys in `INK_MUTED`, values in `INK` with `tabular-nums`. Footer line separated by `RULE_T` + `pt-3`.

### Tagline Row
**Role:** Three-or-four short claims under a headline

Each phrase in nav-role mono uppercase `INK_MUTED`, `py-1 sm:px-3 lg:px-4`, with every item after the first carrying `RULE_L_SM`. **Separate with a rule, never with a `|` character** — a pipe is a glyph, so it brings its own font metrics and sits on a different line-height from the labels either side, which throws the row's vertical centring. The rule is a hairline the exact height of the cell and costs no layout space.

Stacks with `flex-col` below `sm`, where the phrases cannot share a row and a leading rule would dangle at the start of a wrapped line.

### Nav Bar
**Role:** Top navigation

Three cells in one row separated by rules, not padding: wordmark cell (`RULE_R_LG`), centred mono nav (`hidden lg:flex`, `gap-9`), CTA cell (`RULE_L_LG`). The nav reads as a grid because the dividers do the work.

### Stat / Figure
**Role:** A number that is the point of its sentence

Label in label-12-mono uppercase `INK_MUTED`; value in heading-24 `tabular-nums`. Gray for context figures, `BG_BRAND` for the figure the section is actually about. No card chrome — typographic scale alone establishes the metric.

---

## Motion

| Motion | Property | Duration | Trigger |
|---|---|---|---|
| Entrance | `opacity` + `translateY(12px)` | 0.6s, delays 0.05→0.41s | Page load, `data-akta-enter="1".."6"` |
| Reveal | same keyframe | scroll-linked | In view, `data-akta-reveal` |
| Notch reveal | `inset` on `::before` | 0.3s | hover / focus-visible |
| Label roll | `transform` | 0.3s | hover / focus-visible |
| Signal denoise | `opacity`, staggered `calc(var(--akta-i) * 3ms)` | 0.45s | scroll into view, once |
| Logo flip | spring `y` | 0.5s | interval, gated on in-view |

### `data-akta-enter` vs `data-akta-reveal`

**`data-akta-enter="1".."6"`** fires on page load. Use it **only above the fold** — it finishes before anyone scrolls. Steps must be unique and in reading order; duplicated steps make the sequence read as interleaved pairs.

**`data-akta-reveal`** is scroll-linked via `animation-timeline: view()`. Use it for **everything below the fold**, which is every section after the hero. No JavaScript, no observer — a section using it can stay a server component:

```jsx
<div data-akta-reveal>…</div>
```

It sits inside `@supports (animation-timeline: view())`. Where that isn't supported the rule doesn't match and the element renders plain and fully visible — the correct resting state, not a degraded one. Don't add a JS fallback; there is nothing to fall back to.

### Rules

- **Every animation sits inside `@media (prefers-reduced-motion: no-preference)`**, and the resting state outside it must be the meaningful one.
- **Transitions belong on the pseudo-element where possible.** A `transition` on the host competes with `transition-colors` — only one can own the `transition` property.
- **Don't animate the notch with `transform`.** Pushing four corners outward needs a `scale`, and scale is proportional: on a 200×48 button that is ~8px sideways against ~2px vertically. It reads lopsided. Animate `inset` for uniform travel.
- **Derive generated geometry deterministically.** The tick field hashes its index rather than calling `Math.random()`, so server and client markup match.

---

## Accessibility

- Focus rings are **not** replaced by notches. Use `RING_BRAND` on brand controls and `RING_GRAY` on neutral ones — both already carry `outline-2` and the offset; a 1px bracket is too weak to be the only indicator.
- **Outline offset must clear the brackets:** `outline-offset` ≥ `--akta-notch-rest` + `--akta-notch-weight`. On standard buttons that is 6 + 1.5, so `outline-offset-[10px]`. With no notch, `outline-offset-4`.
- Decorative duplicates (`akta-roll-face-next`), schematic graphics (the tick field) and animating wordmarks are `aria-hidden`, with an `sr-only` sentence carrying any claim the graphic makes.
- If an element shows an arrow or a hover affordance, make it a real `<a>` or `<button>`. A hover-animated `<div>` is unreachable by keyboard and the `:focus-visible` half of every interaction is dead.

---

## Imagery

Minimal and schematic. There is no hero photography of people, no illustration, no decorative graphic. The single photographic element is the **background plate** — an abstract texture behind the panel cluster, always partially obscured by opaque panels, functioning as a mat rather than as content. It is decorative, `aria-hidden`, and optional: absent the file, a hatch shows in its place and nothing looks broken.

Everything else that reads as "image" is **drawn from data**: the tick field, the six-bar sentiment chart, the canonical-record list. The visual content *is* the product's output. Icons are thin-stroke geometric marks at 1.5–2px in `INK` or `INK_BRAND`, used only as functional cues — never as decoration.

Customer logos are **typeset, not dropped in as SVGs**, using the system's own heading roles. This keeps the wall coherent when real assets are unavailable, and it means the wall never carries a third typeface.

---

## Agent Prompt Guide

### Quick Reference

| Purpose | Constant |
|---|---|
| Page ground | `BG_CANVAS` |
| Panel surface | `BG_PANEL` |
| Card surface | `BG_CARD` |
| Primary text | `INK` |
| Muted text | `INK_MUTED` |
| Primary action | `cn(BG_BRAND, ON_BRAND)` |
| Data emphasis | `BRAND` |
| Separator | `RULE_T` `RULE_B` `RULE_R` `RULE_Y` `RULE_CELL` `RULE_X_LG` `RULE_L_LG` `RULE_R_LG` `RULE_L_SM` |
| Depth | `SHADOW_PANEL` / `SHADOW_BORDER` / `RING_TILE` |
| Interactive notch | `"akta-notch akta-notch-reveal"` |
| Structural notch | `cn("akta-notch", NOTCH_GRAY)` |
| Grid | `GRID` |
| Radius | **none — never add one** |

### Example Component Prompts

Each assumes the constants from `default.tsx` are in scope — copy the block's token header into the new section, or import it.

1. **Stat band.**
```tsx
<div className={cn("relative", RULE_Y)}>
  <div className={cn(GRID, "grid grid-cols-2 lg:grid-cols-4")} data-akta-reveal>
    {items.map((it) => (
      <div key={it.label} className={cn(
        "px-6 py-8",                                                      // layout
        RULE_CELL,                                                        // separator
        cn("akta-notch", NOTCH_GRAY,                                      // device
           "[--akta-notch-arm:0px] [--akta-notch-inset:0px] [--akta-notch-weight:1px] lg:[--akta-notch-arm:10px]"),
      )}>
        <p className={cn(TYPE_LABEL, INK_MUTED)}>{it.label}</p>
        <p className={cn("mt-1 tabular-nums", TYPE_HEADING_24, INK)}>{it.value}</p>
      </div>
    ))}
  </div>
</div>
```

2. **Primary button.**
```tsx
<a href="#" className={cn(
  "flex items-center px-6 py-3.5",                    // layout
  TYPE_CTA,                                           // type
  cn(BG_BRAND, ON_BRAND),                             // colour
  cn("transition-colors", HOVER_BRAND, RING_BRAND),   // state
  "akta-roll-host akta-notch akta-notch-reveal",      // device
)}>
  <RollLabel>Try for free</RollLabel>
</a>
```

3. **Section header.** `h2` in a `TYPE_HEADING_48` constant you add from the Type Scale table, `INK`, `text-balance`; eyebrow above in `cn(TYPE_LABEL, INK_BRAND)`; body below in a `TYPE_COPY_16` constant with `INK_MUTED max-w-[65ch]`. Wrap in `data-akta-reveal`.

4. **Feature panel.** `cn(BG_PANEL, SHADOW_PANEL, "akta-notch akta-notch-diagonal [--akta-notch-arm:22px] [--akta-notch-inset:10px] w-full lg:max-w-2xl")`. Title bar `cn("px-4 py-2.5", BG_CARD, RULE_B)` with `cn(TYPE_LABEL, INK)` left and `cn(TYPE_LABEL, INK_MUTED)` right. Footer two-cell grid, divider `RULE_R`.

5. **Hatch seam.** `cn("relative z-10", BG_PANEL, RULE_Y)` wrapper, inner `cn(GRID, "akta-hatch h-6 sm:h-9", HATCH_GRAY)`. Closes a section without a divider line.

---

## Design Philosophy

1. **Rules, not boxes.** Structure comes from hairlines and shared edges. A thing is defined by what separates it from its neighbour, not by a container drawn around it.
2. **Square, always.** No radius anywhere. The corner notch is the ornament a rounded corner would otherwise provide.
3. **Blue is a budget.** One accent, spent only on action and on data. Everything structural is gray.
4. **Depth is transparent.** Shadows over borders, so surfaces survive any background — including a photographic plate.
5. **Motion demonstrates.** Animation exists to show a mechanism happening — the filter filtering, the label re-stating — not to decorate an arrival.
6. **Values are utilities, mechanism is CSS.** The moment a stylesheet sets values a utility already covers, the two begin fighting and the markup silently loses.

---

## Similar Brands

- **Vercel** — same Geist pairing, same monospace UI voice, same hairline-rule structure and near-achromatic palette with one accent
- **Linear** — identical tight display tracking and restraint with colour, though Linear rounds its corners where akta refuses to
- **Railway** — same developer-infrastructure register, mono labels over sans headlines, data rendered as the visual content
- **Clay** — closest in subject matter: a data/enrichment API presenting resolved records as the hero graphic rather than describing them
- **Stripe Docs** — same instrument-panel feel from monospace chrome and rule-separated cells, same use of a single blue for emphasis only

---

## Quick Start

Two things travel with this block: **the constants at the top of `default.tsx`** (copy that header into any new akta section) and **the CSS below**, which `shadcn add` writes into your `globals.css` once.

Nothing else is installed — no `@theme`, no `:root` palette, no reset.

### The devices

The six CSS classes go in `@layer components` so Tailwind utilities always win over them. See **Signature Devices** above for their full API; the shipped `css` block in `config.json` is the source of truth.

```css
@layer components {
  .akta-notch { position: relative; --akta-notch-color: oklch(0.488 0.243 264.376); … }
  .dark .akta-notch { --akta-notch-color: oklch(0.546 0.245 262.881) }
  .akta-notch::before { /* full border + four-layer corner mask */ }
  .akta-hatch { /* repeating 45° gradient */ }
  .akta-roll  { /* clip + two faces */ }
  .akta-tick  { /* noise/armed/run states */ }
  .akta-plate { /* image layer over hatch fallback */ }
  [data-akta-enter] { animation: akta-enter 0.6s var(--akta-ease) backwards }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    [data-akta-reveal] {
      animation: akta-enter 1s linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 28%;
    }
  }
}
```
````
