/* Shape vocabulary (REDESIGN-PLAN.md §5). Forms, fills, tones, sizes, clusters and edges.

   A shape is one element with classes, placed by a cluster or an edge class, never by
   the page:
     <span class="shape sh-quarter t-3 sz-m mo-turn" aria-hidden="true"></span>
     <svg class="shape sh-step t-4 sz-s" aria-hidden="true" focusable="false"><use href="/ds/shapes.svg#step"></use></svg>

   Rules kept here:
   - Shapes never take a transform. Transform is the motion channel (shapes.js, GSAP), so
     orientation comes from border-radius and clip-path, and placement from insets.
   - Colours come only from the field roles (--shape-1…4, tokens.css), so a shape on any
     field takes that field's allowed tone without overrides.
   - Shapes are decoration: aria-hidden in the markup, no pointer events, never behind text. */

@layer shapes {
  /* Read by shapes.js through getComputedStyle, so pages need no data attributes and JS
     never selects a styling class. Not inherited: only the shape itself carries them. */
  @property --sh-form { syntax: "*"; inherits: false; }
  @property --sh-motion { syntax: "*"; inherits: false; }
  @property --sh-edge { syntax: "*"; inherits: false; }

  /* Split partner for t-3. On ink, t-3 already resolves to saffron, so its
     partner turns to blush; everywhere else it is saffron. */
  :root, .f-bone, .f-blush, .f-cobalt { --sh-partner-3: var(--shape-1); }
  .f-ink { --sh-partner-3: var(--shape-2); }

  /* ---- Base -------------------------------------------------------------------------
     Sizes tie to the page grid (§5.3): one column is the window's column, the same sum
     as --col in layout.css, so a shape keeps its size wherever it sits. */
  .shape {
    --sh-col: calc((min(100vw, var(--content-max)) - 2 * var(--margin) - (var(--cols) - 1) * var(--gutter)) / var(--cols));
    --sh-s: var(--sh-col);
    --sh-m: calc(2 * var(--sh-col) + var(--gutter));
    --sh-l: calc(3 * var(--sh-col) + 2 * var(--gutter));
    --sh-xl: calc(5 * var(--sh-col) + 4 * var(--gutter));
    --sh-size: var(--sh-m);
    --sh-tone: var(--shape-1);
    --sh-tone-2: var(--shape-4);
    --sh-paint: var(--sh-tone);
    display: block;
    flex: none;
    box-sizing: border-box;
    inline-size: min(var(--sh-size), var(--sh-cap, var(--sh-size)));
    max-inline-size: none;
    aspect-ratio: 1;
    background: var(--sh-paint);
    color: var(--sh-tone);
    pointer-events: none;
    user-select: none;
  }

  /* ---- Forms (§5.1): three primitives and their cuts ---------------------------------- */
  /* Circle */
  .sh-sun { --sh-form: sun; border-radius: 50%; }
  .sh-moon { --sh-form: moon; aspect-ratio: 2; border-radius: var(--r-pill) var(--r-pill) 0 0; }
  .sh-quarter { --sh-form: quarter; border-radius: 100% 0 0 0; }
  .sh-ring { --sh-form: ring; --sh-paint: transparent; border-radius: 50%; border: var(--ring) solid var(--sh-tone); }
  .sh-split { --sh-form: split; --sh-paint: linear-gradient(90deg, var(--sh-tone) 50%, var(--sh-tone-2) 50%); border-radius: 50%; }
  .sh-arch { --sh-form: arch; aspect-ratio: 3 / 4; border-radius: var(--r-pill) var(--r-pill) 0 0; }
  /* Square */
  .sh-diamond {
    --sh-form: diamond;
    --sh-t: calc(var(--ring) * 1.4142);
    clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
    --sh-hollow: polygon(evenodd, 50% 0, 100% 50%, 50% 100%, 0 50%, 50% 0,
      50% var(--sh-t), var(--sh-t) 50%, 50% calc(100% - var(--sh-t)), calc(100% - var(--sh-t)) 50%, 50% var(--sh-t));
  }
  .sh-step { --sh-form: step; --sh-paint: none; fill: currentColor; overflow: visible; }
  /* Triangle: equilateral, point up; wedge: right angle at the bottom left */
  .sh-triangle {
    --sh-form: triangle;
    --sh-t: var(--ring);
    aspect-ratio: 1.1547;
    clip-path: polygon(50% 0, 100% 100%, 0 100%);
    --sh-hollow: polygon(evenodd, 50% 0, 100% 100%, 0 100%, 50% 0,
      50% calc(2 * var(--sh-t)), calc(1.732 * var(--sh-t)) calc(100% - var(--sh-t)),
      calc(100% - 1.732 * var(--sh-t)) calc(100% - var(--sh-t)), 50% calc(2 * var(--sh-t)));
  }
  .sh-wedge {
    --sh-form: wedge;
    --sh-t: var(--ring);
    clip-path: polygon(0 0, 100% 100%, 0 100%);
    --sh-hollow: polygon(evenodd, 0 0, 100% 100%, 0 100%, 0 0,
      var(--sh-t) calc(2.4142 * var(--sh-t)), calc(100% - 2.4142 * var(--sh-t)) calc(100% - var(--sh-t)),
      var(--sh-t) calc(100% - var(--sh-t)), var(--sh-t) calc(2.4142 * var(--sh-t)));
  }

  /* ---- Fills (§5.3) ---------------------------------------------------------------- */
  .fill-solid { --sh-paint: var(--sh-tone); }
  .fill-outline { --sh-paint: transparent; border: var(--ring) solid var(--sh-tone); }
  .fill-outline:is(.sh-diamond, .sh-triangle, .sh-wedge) { --sh-paint: var(--sh-tone); border: 0; clip-path: var(--sh-hollow); }
  .fill-outline.sh-step { --sh-paint: none; border: 0; fill: none; stroke: currentColor; stroke-width: var(--ring); }
  .fill-split { --sh-paint: linear-gradient(90deg, var(--sh-tone) 50%, var(--sh-tone-2) 50%); }

  /* ---- Tones (§4.1): roles, not colours. Each resolves through the field it sits on
     (--shape-1…4 in tokens.css):
              t-1      t-2      t-3      t-4
     bone     saffron  blush    cobalt   ink
     blush    saffron  cobalt   ink      ink
     cobalt   saffron  blush    bone     bone
     ink      saffron  blush    saffron  blush                                          */
  .t-1 { --sh-tone: var(--shape-1); --sh-tone-2: var(--shape-4); }
  .t-2 { --sh-tone: var(--shape-2); --sh-tone-2: var(--shape-1); }
  .t-3 { --sh-tone: var(--shape-3); --sh-tone-2: var(--sh-partner-3); }
  .t-4 { --sh-tone: var(--shape-4); --sh-tone-2: var(--shape-1); }

  /* ---- Sizes (§5.3): S one column, M two, L three, XL five (edges only) --------------- */
  .sz-s { --sh-size: var(--sh-s); }
  .sz-m { --sh-size: var(--sh-m); }
  .sz-l { --sh-size: var(--sh-l); }
  .sz-xl { --sh-size: var(--sh-xl); }
  @media (width < 761px) {
    .sz-xl { --sh-size: var(--sh-l); }   /* only L on mobile (§5.4) */
  }

  /* ---- Motion kinds (§7.2): read by shapes.js; without it the shape simply rests ------ */
  .mo-drift { --sh-motion: drift; }
  .mo-rise { --sh-motion: rise; }
  .mo-turn { --sh-motion: turn; }
  .mo-sway { --sh-motion: sway; }
  .mo-grow { --sh-motion: grow; }
  .mo-orbit { --sh-motion: orbit; }

  /* ---- Clusters (§5.4) ------------------------------------------------------------
     The cluster is the media's wrapper. Its media child keeps its place; each shape takes
     the slot named by its form. Insets are percentages of the media, and --sh-cap keeps a
     shape in proportion when the media is narrower than the grid sizes assume.
     Use two or three shapes per cluster: the slots are a menu, not a set. */
  :is(.cluster-sun-a, .cluster-sun-b, .cluster-sun-c, .cluster-pair-a, .cluster-pair-b, .cluster-pair-c, .cluster-card-a, .cluster-card-b) {
    position: relative;
    isolation: isolate;
  }
  :is(.cluster-sun-a, .cluster-sun-b, .cluster-sun-c, .cluster-pair-a, .cluster-pair-b, .cluster-pair-c, .cluster-card-a, .cluster-card-b) > :not(.shape) {
    position: relative;
    z-index: 1;
  }
  :is(.cluster-sun-a, .cluster-sun-b, .cluster-sun-c, .cluster-pair-a, .cluster-pair-b, .cluster-pair-c, .cluster-card-a, .cluster-card-b) > .shape {
    position: absolute;
    z-index: 0;   /* behind the media; slots marked "front" sit on it */
  }

  /* sun-a · hero portrait in an arch: sun behind (top end), moon and quarter in front
     (bottom start, bottom end); ring behind (start) or one accent (start). */
  .cluster-sun-a > .sh-sun { --sh-cap: 60%; inset-block-start: -7%; inset-inline-end: -12%; }
  .cluster-sun-a > .sh-moon { --sh-cap: 74%; inset-block-end: -10%; inset-inline-start: -18%; z-index: 2; }
  .cluster-sun-a > .sh-quarter { --sh-cap: 28%; inset-block-end: 0; inset-inline-end: -8%; z-index: 2; border-radius: 0 100% 0 0; }
  .cluster-sun-a > .sh-ring { --sh-cap: 38%; inset-block-start: 14%; inset-inline-start: -16%; }
  .cluster-sun-a > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) { --sh-cap: 17%; inset-block-start: 56%; inset-inline-start: -14%; z-index: 2; }

  /* sun-b · about portrait in a round frame: sun in front (bottom end), a quarter behind
     anchoring the top-start corner, or one accent behind (top end). */
  .cluster-sun-b > .sh-sun { --sh-cap: 38%; inset-block-end: -6%; inset-inline-end: -8%; z-index: 2; }
  .cluster-sun-b > .sh-quarter { --sh-cap: 46%; inset-block-start: -8%; inset-inline-start: -8%; }
  /* The accent stands out at the corner, at least half of it clear of the picture (C77: at -4%/-2%
     three quarters hid behind it and read as a clipping accident). */
  .cluster-sun-b > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) { --sh-cap: 18%; inset-block-start: -6%; inset-inline-end: -7%; }

  /* sun-c · portrait with the sun on the start side, for layouts where the text sits at
     the end: sun behind (top start), moon in front (bottom end), quarter in front (bottom
     start); a step behind or one accent in front (middle end). */
  .cluster-sun-c > .sh-sun { --sh-cap: 60%; inset-block-start: -7%; inset-inline-start: -12%; }
  .cluster-sun-c > .sh-moon { --sh-cap: 74%; inset-block-end: -10%; inset-inline-end: -18%; z-index: 2; }
  .cluster-sun-c > .sh-quarter { --sh-cap: 28%; inset-block-end: 0; inset-inline-start: -8%; z-index: 2; border-radius: 100% 0 0 0; }
  .cluster-sun-c > .sh-step { --sh-cap: 34%; inset-block-start: 30%; inset-inline-end: -20%; }
  .cluster-sun-c > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) { --sh-cap: 17%; inset-block-start: 42%; inset-inline-end: -14%; z-index: 2; }

  /* Pairs hold UI screens, which are never covered (§4.5): every pair slot sits behind
     the screens and shows only where it stands out of them. */
  /* pair-a · two phone screens standing on a moon: the moon spans the pair; a quarter
     (top end) or one accent (start). */
  .cluster-pair-a > .sh-moon { inset-inline: 0; inset-block-end: -2%; inline-size: auto; }
  .cluster-pair-a > .sh-quarter { --sh-cap: 30%; inset-block-start: -10%; inset-inline-end: -10%; border-radius: 0 0 0 100%; }
  .cluster-pair-a > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) { --sh-cap: 14%; inset-block-start: 30%; inset-inline-start: -10%; }
  /* In a case device stage (c-pair: Pomli, Care to Cure) the screens stand on the moon's flat
     base, and the moon reaches a tenth past the pair at each side, so it shows round the screens
     and stays lower than them: their tops rise above its curve (T9, T10). On phones the media
     starts at the page margin, so the moon stops a step inside the window's edges. */
  .cluster-pair-a.c-pair > .sh-moon { inset-inline: -10%; inset-block-end: 0; }
  @media (width < 761px) {
    .cluster-pair-a.c-pair > .sh-moon { inset-inline: max(-10%, calc(var(--s2) - var(--margin))); }
  }

  /* pair-b · two phone screens under an arch that echoes their arched frames: the arch
     (top centre), a step (bottom start), or one accent (top end). */
  .cluster-pair-b > .sh-arch { --sh-cap: 78%; inset-block-start: -7%; inset-inline: 0; margin-inline: auto; }
  .cluster-pair-b > .sh-step { --sh-cap: 26%; inset-block-end: 4%; inset-inline-start: -14%; }
  .cluster-pair-b > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) { --sh-cap: 15%; inset-block-start: 2%; inset-inline-end: -4%; z-index: 0; }

  /* pair-c · two phone screens standing in an arch doorway (C46): the arch fills the pair's
     box, so it rises above the screens and its base is their foot line (the box takes its 3:4
     shape from the component, c-pair--door, 2:3 since C56); one accent in the top-end corner, outside the curve. */
  .cluster-pair-c > .sh-arch { inset: 0; inline-size: auto; aspect-ratio: auto; }
  .cluster-pair-c > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) { --sh-cap: 12%; inset-block-start: 0; inset-inline-end: 0; }

  /* card-a, card-b · Me & AI timeline cards (C61). The cluster is a column of cards
     (c-timeline-work) and its one shape belongs to the first card: insets are the shape's own
     size, so it keeps its place whatever the card's height. Cards are opaque, so the shape shows
     only where it stands out of the card, never under the card's text.
       card-a  a quarter behind the top-end corner, its curve rising out of it
       card-b  a moon rising out of the top edge near the end, two fifths of it behind the card
     Both keep 24px clear of the card above through their whole movement (§15 R6). Wide screens only: on phones a card fills the column and the shape would leave the screen. */
  /* Its end overhang stops short of the window edge: the quarter turns up to 12° (mo-turn), which
     widens it by 0.093 of its size each side, so it keeps 0.11 of its size inside the window.
     At the page's end column it had stood 1–3px past the edge, so Me & AI scrolled sideways (C90). */
  .cluster-card-a > .sh-quarter {
    inset-block-start: calc(var(--sh-size) * -0.25);
    inset-inline-end: max(calc(var(--sh-size) * -0.25), calc(var(--sh-size) * 0.11 - var(--margin)));
    border-radius: 0 100% 0 0;
  }
  .cluster-card-b > .sh-moon { inset-block-start: calc(var(--sh-size) * -0.3); inset-inline-end: var(--s7); }
  @media (width < 761px) {
    :is(.cluster-card-a, .cluster-card-b) > .shape { display: none; }
  }

  /* On phones a media column starts at the page margin, so a start-side slot reaches at most
     to a step inside the window's edge: only L and XL edge shapes may leave the screen (§5.4;
     the GHK split, the Ayala triangle and the Rapha wedge were 6–20px off it at 390 and 320). */
  @media (width < 761px) {
    .cluster-sun-a > :is(.sh-ring, .sh-split, .sh-diamond, .sh-triangle, .sh-wedge),
    .cluster-sun-c > .sh-quarter,
    .cluster-sun-b > .sh-quarter,
    .cluster-pair-a > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) {
      inset-inline-start: max(var(--sh-start, -8%), calc(var(--s2) - var(--margin)));
    }
    .cluster-sun-a > .sh-ring { --sh-start: -16%; }
    .cluster-sun-a > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) { --sh-start: -14%; }
    .cluster-pair-a > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) { --sh-start: -10%; }
    /* and an end-side accent at most a step outside the media column, inside the window */
    .cluster-sun-b > :is(.sh-split, .sh-diamond, .sh-triangle, .sh-wedge) { inset-inline-end: max(-7%, calc(var(--s2) - var(--margin))); }
    /* A pair that takes the full width (Care to Cure) keeps its end quarter on screen too */
    .cluster-pair-a > .sh-quarter { inset-inline-end: max(-10%, calc(var(--s2) - var(--margin))); }
  }

  /* field-end · opener B (c-opener--field, T7): one still L shape in the field's end columns,
     level with the lede. The component places this box in the grid (cols 10–12 of the intro's
     row); the shape fills its width and sits at its end. Below 761px there is no room beside the
     intro: the box is a band between the intro and the media, and the shape stands 40% off the
     window's end edge (§5.4 edge shapes), its lower part behind the media, which stands in front.
     Still: no motion class, no slide (a moving shape here reached the intro and the window edge). */
  .cluster-field-end { position: relative; display: grid; justify-items: end; align-items: center; pointer-events: none; }
  .cluster-field-end > .shape { --sh-size: var(--sh-l); position: relative; z-index: -1; }
  .cluster-field-end > .sh-quarter { border-radius: 100% 0 0 0; }
  @media (width < 761px) {
    .cluster-field-end > .shape {
      margin-inline-end: calc(-1 * var(--margin) - 0.4 * var(--sh-size));
      margin-block-end: calc(-0.6 * var(--sh-size));
    }
  }

  /* field-foot · a full-bleed field with a panel hanging across its foot (contact, S1): one still
     M shape centred on the field's bottom edge, half off the window's start edge, so it stands
     across the foot as the panel does, on the field and below it. The slot is a strip the width
     of the field and the height of the shape (--sh-foot: M, the same column sum as .shape's),
     its middle on the foot line; it clips the half past the window, so the field itself never
     clips (its panel hangs out of it). Wide screens only: the page marks the slot u-wide-only,
     since on phones the panel takes the full width and the shape would sit in the margin. */
  :where(:has(> .cluster-field-foot)) { position: relative; }
  .cluster-field-foot {
    --sh-foot: calc(2 * (min(100vw, var(--content-max)) - 2 * var(--margin) - (var(--cols) - 1) * var(--gutter)) / var(--cols) + var(--gutter));
    position: absolute;
    inset-inline: 0;
    inset-block-end: calc(-0.5 * var(--sh-foot));
    block-size: var(--sh-foot);
    overflow: clip;
    pointer-events: none;
  }
  .cluster-field-foot > .shape { --sh-size: var(--sh-foot); margin-inline-start: calc(-0.5 * var(--sh-size)); }

  /* line-orbit · where a page's line starts (C69, the Me & AI opener): the line loops once round
     a sun, as it loops round the home portrait. In this order: an L sun (data-line-loop), a dot
     where the line starts (S/4, data-line-start) and a ring on the orbit (S/2, data-line-stop).
     The orbit is the circle line.js draws round a loop: the sun's radius plus 28px (line.js's
     loop gap on wide screens). Its right edge is column 4's centre in a six-column area, the
     timeline's rail (C53), so the line leaves the orbit heading straight down onto the rail. The
     dot stands on that rail level with the orbit's top, so the line drops from it into the turn;
     the ring sits on the orbit at half past ten, a node on the line (§5.2), filled with the
     ground so the line passes behind it. Three shapes (§5.4). Still: the ring scales only as the
     line arrives (line.js), and nothing drifts. */
  .cluster-line-orbit {
    --orbit-col: calc((min(100vw, var(--content-max)) - 2 * var(--margin) - (var(--cols) - 1) * var(--gutter)) / var(--cols));
    --orbit-sun: calc(3 * var(--orbit-col) + 2 * var(--gutter));
    --orbit: calc(var(--orbit-sun) / 2 + 28px);
    --orbit-x: calc((100% - 5 * var(--gutter)) / 6 * 3.5 + 3 * var(--gutter) - var(--orbit));
    position: relative;
    block-size: calc(2 * var(--orbit));
    pointer-events: none;
  }
  .cluster-line-orbit > .shape { position: absolute; inset-inline-start: calc(var(--orbit-x) + var(--orbit) * var(--at-x) - var(--sh-size) / 2); inset-block-start: calc(var(--orbit) + var(--orbit) * var(--at-y) - var(--sh-size) / 2); }
  .cluster-line-orbit > :nth-child(1) { --sh-size: var(--orbit-sun); --at-x: 0; --at-y: 0; }
  .cluster-line-orbit > :nth-child(2) { --sh-size: calc(var(--sh-s) / 4); --at-x: 0.7071; --at-y: 0.7071; }
  .cluster-line-orbit > :nth-child(3) { --sh-size: calc(var(--sh-s) / 2); --at-x: -0.7071; --at-y: -0.7071; --sh-paint: var(--ground); }

  /* ---- Edge shapes (§5.4) -----------------------------------------------------------
     An L or XL shape pinned to the window edge, 40% off screen in its place, and at most 60% at the start of the slide (the rule allows 30–60%). While the band rises from the bottom of the window to 40% up, the slide is still under way.
     The parent must be a full-bleed band (a field or section); it becomes the positioning
     and clipping box, and the shape sits under its text. One per screen; L only on mobile
     (sz-xl already drops to L there). The band clips, so there is no sideways scroll. */
  :where(:has(> .edge-left, > .edge-right)) {
    position: relative;
    isolation: isolate;
    overflow: clip;   /* the band's own edges are the window's: the shape never spills into its neighbours */
  }
  .edge-left, .edge-right {
    position: absolute;
    z-index: -1;
    grid-column: 1 / -1;   /* in an .l-grid, the whole window width */
    inset-block: 0;
    margin-block: auto;
    inline-size: var(--sh-size);
  }
  :is(.edge-left, .edge-right):not(.sz-xl) { --sh-size: var(--sh-l); }
  .edge-left { --sh-edge: left; inset-inline-start: calc(-0.4 * var(--sh-size)); }
  .edge-right { --sh-edge: right; inset-inline-end: calc(-0.4 * var(--sh-size)); }
  /* A quarter at the edge turns its curve to the page, so the cut stays on screen. */
  .edge-left.sh-quarter { border-radius: 0 100% 0 0; }
  .edge-right.sh-quarter { border-radius: 100% 0 0 0; }
  .edge-top { inset-block: var(--space-component) auto; }
  .edge-bottom { inset-block: auto var(--space-component); }

  /* Flush top (the 404, S1): an edge shape that heads its band, its top on the band's top. The
     band's text starts below it (the page pads by the shape's size: L, or XL wide). Below 761px it
     steps down to S, so at 390 the title starts high and the ways onward fit the first screen
     (C86; M left the band top-heavy); it stays 40% off the edge. There it also rises 4px past the band's top: a line that starts on the margin rail
     at the top then starts behind it, and the clip margin keeps its top round where supported. */
  .edge-top-flush { inset-block: 0 auto; margin-block: 0; }
  @media (width < 761px) {
    :where(:has(> .edge-top-flush)) { overflow-clip-margin: var(--s1); }
    :is(.edge-left, .edge-right).edge-top-flush { --sh-size: var(--sh-s); inset-block-start: calc(-1 * var(--s1)); }
  }
}
