/* ============================================================
   AIGYM — shared design tokens, motion and section header.

   The subject is inspection: auditing, correcting and gating AI
   output before it ships. Every page is set like an inspection
   record — ruled, aligned, tabular, one ink, one signal colour.
   Headings are 明朝 because an established Japanese company sets
   its voice in 明朝.

   Load this first, then the page's own stylesheet. Page styles
   consume the tokens below rather than restating colours.
   ============================================================ */

:root {
    /* Ink ramp */
    --ink:        #17202D;
    --ink-mid:    #48566A;
    --ink-soft:   #5F6C7D;
    --ink-faint:  #A8B3C0;

    /* Ground + rules */
    --paper:      #FFFFFF;
    --paper-2:    #F3F5F4;
    --paper-3:    #ECEFEE;
    --rule:       #DCE1E6;
    --rule-firm:  #B7C0C9;

    /* One signal. It means "actionable" or "newest" — never decoration. */
    --signal:      #0A6C7D;
    --signal-deep: #07505E;
    --signal-wash: #E9F1F2;

    /* Ink grounds */
    --ink-ground:  #0F172A;

    --serif: 'Noto Serif JP', 'Hiragino Mincho ProN', 'Yu Mincho', serif;
    --sans:  'Noto Sans JP', 'Hiragino Sans', 'Yu Gothic', sans-serif;

    --ease: cubic-bezier(.22, .61, .36, 1);
    --ease-out: cubic-bezier(.16, 1, .3, 1);
    --step: 70ms;

    /* Japanese runs wide, so ~38em ≈ 38 full-width characters. */
    --measure: 38em;
}

/* ── Base ────────────────────────────────────────────── */

.aigym-page {
    font-family: var(--sans);
    color: var(--ink-mid);
    font-size: 16px;
    line-height: 1.95;
    letter-spacing: .01em;
    background: var(--paper);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* :where() keeps these at single-class specificity, so a page's own
   classes override them by source order instead of losing to a
   descendant selector. Without it, a white heading on an ink band
   silently loses to the base ink colour. */
.aigym-page :where(h1, h2, h3, h4, h5) {
    font-family: var(--serif);
    color: var(--ink);
    font-weight: 700;
    line-height: 1.5;
    letter-spacing: .015em;
    /* Proportional kana: real Japanese typesetting, not default metrics. */
    font-feature-settings: "palt" 1;
}

.aigym-page :where(p) { line-height: 1.95; }
.aigym-page :where(a) { color: var(--signal); }

/* Figures never jitter while counting. */
[data-count],
.aigym-figure,
.aigym-page time {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

:focus-visible {
    outline: 2px solid var(--signal);
    outline-offset: 2px;
}

/* ── Motion system ───────────────────────────────────
   Four verbs, one easing, one stagger unit:
     rule  — a rule draws, so the page's spine assembles
     rise  — a block clips upward into place
     sweep — a list row arrives from the margin
     count — figures count up, because the figures are the point

   Every hidden state is gated behind html.js-anim, set by
   aigym_motion.js in the document head so the first paint already
   has the right state. That script drops the gate again if the
   engine never starts, so a JS failure leaves a readable page
   rather than a blank one.
   ──────────────────────────────────────────────────── */

[data-anim] { --d: 0ms; }

html.js-anim [data-anim="rise"] {
    opacity: 0;
    transform: translateY(16px);
    clip-path: inset(0 0 10px 0);
    transition:
        opacity .75s var(--ease) var(--d),
        transform .75s var(--ease) var(--d),
        clip-path .75s var(--ease) var(--d);
}
html.js-anim [data-anim="rise"].is-in {
    opacity: 1;
    transform: none;
    clip-path: inset(0 0 0 0);
}

html.js-anim [data-anim="rule"] {
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform .95s var(--ease-out) var(--d);
}
html.js-anim [data-anim="rule"].is-in { transform: scaleX(1); }

html.js-anim [data-anim="sweep"] {
    opacity: 0;
    transform: translateX(-14px);
    transition:
        opacity .62s var(--ease) var(--d),
        transform .62s var(--ease) var(--d);
}
html.js-anim [data-anim="sweep"].is-in { opacity: 1; transform: none; }

html.js-anim [data-anim="fade"] {
    opacity: 0;
    transition: opacity .8s var(--ease) var(--d);
}
html.js-anim [data-anim="fade"].is-in { opacity: 1; }

/* A headline that sets itself, line by line, as the page opens. */
.aigym-line {
    display: block;
    overflow: hidden;
    white-space: normal;
    /* keep-all is a Korean rule: it forbids breaks inside a CJK run, so a
       Japanese line becomes one unbreakable word and blows past the column.
       Japanese wraps with normal + line-break:strict, which applies 禁則処理. */
    word-break: normal;
    line-break: strict;
    overflow-wrap: anywhere;
    padding-bottom: .04em;
}
.aigym-line > .aigym-ink { display: block; }
html.js-anim .aigym-line > .aigym-ink {
    transform: translateY(108%);
    transition: transform .95s var(--ease-out) var(--d);
}
html.js-anim.is-ready .aigym-line > .aigym-ink { transform: none; }

/* ── Section header ──────────────────────────────────
   Japanese title, a rule running out to the margin, the English
   name at the rule's end. Replaces the tracked-out label stacked
   above every heading. 和英併記 is a Japanese-site convention, so
   the English page drops the label and lets the rule run out.
   ──────────────────────────────────────────────────── */

.sec-head {
    display: flex;
    align-items: baseline;
    gap: 1.5rem;
    margin: 0 0 1.25rem;
}
.sec-head-ja {
    font-family: var(--serif);
    font-weight: 700;
    font-size: clamp(1.45rem, 3vw, 2rem);
    color: var(--ink);
    letter-spacing: .04em;
    line-height: 1.4;
    margin: 0;
    white-space: nowrap;
    font-feature-settings: "palt" 1;
}
.sec-head-line {
    flex: 1 1 auto;
    height: 1px;
    min-width: 2rem;
    background: var(--rule-firm);
    align-self: center;
}
.sec-head-en {
    flex: 0 0 auto;
    font-family: var(--sans);
    font-size: .76rem;
    font-weight: 500;
    color: var(--ink-soft);
    letter-spacing: .08em;
    white-space: nowrap;
}
html[lang="en"] .sec-head-en { display: none; }

.sec-lede {
    max-width: var(--measure);
    margin: 0 0 3rem;
    color: var(--ink-mid);
    font-size: .95rem;
    line-height: 1.95;
}
.sec-head + .sec-lede { margin-top: 0; }

/* A long Japanese title plus a nowrap English label overruns a phone column,
   and the page's overflow:hidden then clips it. On narrow screens the title
   wraps, the rule keeps its own row, and the bilingual label steps aside. */
@media (max-width: 860px) {
    .sec-head { gap: .5rem .9rem; flex-wrap: wrap; }
    .sec-head-ja {
        font-size: clamp(1.15rem, 4.4vw, 1.45rem);
        letter-spacing: .03em;
        white-space: normal;
        flex: 0 1 auto;
        min-width: 0;
    }
    .sec-head-line { min-width: 1.5rem; }
    .sec-head-en { display: none; }
}

/* ── Quality gate field ──────────────────────────────
   The threshold the marks are measured against. It is a real hairline in
   the DOM rather than texture in the canvas, so it stays one pixel at any
   density and can carry set type. ──────────────────── */

.gate-rule {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 0;
    pointer-events: none;
    z-index: 1;
}
.gate-rule-line {
    position: absolute;
    top: 8%;
    bottom: 8%;
    left: 0;
    width: 1px;
    background: linear-gradient(
        to bottom,
        transparent,
        var(--rule-firm) 12%,
        var(--rule-firm) 88%,
        transparent
    );
}
.gate-rule-label {
    position: absolute;
    top: 8%;
    left: 10px;
    font-family: var(--sans);
    font-size: .68rem;
    font-weight: 500;
    letter-spacing: .1em;
    color: var(--ink-soft);
    white-space: nowrap;
    opacity: .75;
}
.gate-rule--dark .gate-rule-line {
    background: linear-gradient(
        to bottom,
        transparent,
        rgba(255, 255, 255, .28) 12%,
        rgba(255, 255, 255, .28) 88%,
        transparent
    );
}
.gate-rule--dark .gate-rule-label { color: rgba(255, 255, 255, .55); }

/* On a phone the gate crosses behind the copy, so it steps back to a trace. */
@media (max-width: 767px) {
    .gate-rule-label { display: none; }
    .gate-rule-line { opacity: .55; }
}

@media (prefers-reduced-motion: reduce) {
    .gate-rule-line { opacity: .45; }
}

/* The stage the field is given when it is the subject rather than ambience. */
.gate-stage {
    position: relative;
    margin: 0 0 2.5rem;
    border: 1px solid var(--rule);
    background: var(--paper);
    overflow: hidden;
}
.gate-stage-canvas {
    display: block;
    width: 100%;
    height: clamp(230px, 30vw, 360px);
}
.gate-stage-caption {
    border-top: 1px solid var(--rule);
    padding: .9rem 1.25rem;
    font-size: .84rem;
    line-height: 1.85;
    color: var(--ink-mid);
    background: var(--paper);
    position: relative;
    z-index: 2;
}
@media (max-width: 767px) {
    .gate-stage-canvas { height: clamp(190px, 46vw, 260px); }
    .gate-stage-caption { padding: .8rem 1rem; font-size: .8rem; }
}

/* ── Shared marks ────────────────────────────────────
   One mark means "item in a list" across every page: a short
   signal rule, never an arrow glyph or a coloured disc. ─────── */

.aigym-mark {
    display: inline-block;
    flex: 0 0 auto;
    font-size: 0;
    width: 6px;
    height: 1px;
    background: var(--signal);
    margin: .9em .7rem 0 0;
}

/* A link whose underline draws on hover, instead of a trailing arrow. */
.aigym-link {
    color: var(--signal);
    text-decoration: none;
    font-weight: 700;
    background-image: linear-gradient(var(--signal), var(--signal));
    background-size: 0% 1px;
    background-position: 0 100%;
    background-repeat: no-repeat;
    transition: background-size .3s var(--ease);
}
.aigym-link:hover,
.aigym-link:focus-visible { background-size: 100% 1px; }

/* ── Reduced motion ──────────────────────────────────── */

/* Printing has no scroll, so nothing may stay mid-reveal. */
@media print {
    html.js-anim [data-anim],
    html.js-anim [data-anim].is-in {
        opacity: 1 !important;
        transform: none !important;
        clip-path: none !important;
    }
    html.js-anim .aigym-line > .aigym-ink { transform: none !important; }
}

@media (prefers-reduced-motion: reduce) {
    html.js-anim [data-anim],
    html.js-anim [data-anim].is-in {
        opacity: 1 !important;
        transform: none !important;
        clip-path: none !important;
        transition: none !important;
    }
    html.js-anim .aigym-line > .aigym-ink {
        transform: none !important;
        transition: none !important;
    }
}
