/* HAUTE LUMIÈRE — the sky, standalone.
 *
 * The room a page sits in, lit by the reader's own sun. Self-contained: it needs
 * only --paper, --ink, --gold and --veil, which every surface in this house
 * already declares, and falls back to dusk where they are missing. Drop it on
 * any page beside its own stylesheet.
 *
 *   <link rel="stylesheet" href="/_hl/sky.css">
 *   <script type="module">import s from "/_hl/sky.js"; s({weather:true})</script>
 *   <body class="hl-room"><div class="hl-grain"></div> …
 *
 * THE RULE: the room breathes, the page does not. Atmosphere belongs to the
 * ground behind content. Nothing here touches type, radius, rule or plate.
 *
 * Generated from tokens/atmosphere.css + tokens/sky.css — edit those.
 */

:root{
  /* Fallbacks, so a surface with no token layer still gets a room. Dusk. */
  --paper:var(--paper, #FBF4EA);
  --ink:var(--ink, #33201A);
  --gold:var(--gold, #D69A4C);
  --veil:var(--veil, 51,32,26);
  --grain-opacity:var(--grain-opacity, .03);
  --grain-blend:var(--grain-blend, multiply);
  --grain-url:var(--grain-url, url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.03'/%3E%3C/svg%3E"));

  /* The shadow follows the light. Unset, this is the old fixed shadow exactly. */
  --light-x:0; --light-y:0.45; --sun:0.5;
  --throw:calc(1.55 - (var(--sun) * 0.9));
  --shadow-sheet:
    calc(var(--light-x) * -34px * var(--throw))
    calc(var(--light-y) * -34px * var(--throw) + 30px * var(--throw))
    calc(90px * var(--throw)) -40px rgba(var(--veil),.5);
  --shadow-plate:
    calc(var(--light-x) * -13px * var(--throw))
    calc(var(--light-y) * -13px * var(--throw) + 12px * var(--throw))
    calc(38px * var(--throw)) -20px rgba(var(--veil),.42);
}

/* Pictures lift off the page, and away from the sun. */
.hl-room :is(img, figure, picture, .plate) { box-shadow:var(--shadow-plate); }


:root{
  /* Resting respiration. Fifteen seconds is the number Resonance
     arrived at and it is right — slower than a sigh, faster than
     forgetting the surface is there at all. */
  --breath:15s;
  --breath-ease:ease-in-out /* @kind other */;

  --bloom-blur:60px;
  --bloom-size:min(94vw, 1080px);
  /* Light registers carry the room openly; dark ones only suggest it,
     or the blooms read as haze rather than air. */
  --bloom-opacity:.34;
  --bloom-drift:1.05 /* @kind other */;

  --glass-blur:12px;
  --hush-blur:20px;

  /* Bloom — gold, as light rather than as fill. Punctuation on the one
     live thing on a surface, and never more than one at a time. */
  --glow-gold:0 0 24px rgba(197,160,89,.25) /* @kind other */;
}

html[data-register="vespers"],
html[data-register="midnight"],
html[data-register="nocturne"]{ --bloom-opacity:.16; }

/* ── the room ─────────────────────────────────────────────────
   Put .hl-room on whatever holds the page — body, a section, a
   window. It paints two drifting blooms and the paper noise, and
   nothing else. It has no border, no radius and no shadow: it is
   air, and air does not have edges. */
.hl-room{ position:relative; isolation:isolate; background:var(--paper); }

.hl-room::before,
.hl-room::after{
  content:""; position:absolute; z-index:-1; pointer-events:none;
  width:var(--bloom-size); height:var(--bloom-size);
  border-radius:50%;                    /* a bloom is not a surface */
  filter:blur(var(--bloom-blur));
  opacity:var(--bloom-opacity);
  will-change:transform;
  animation:hl-breathe var(--breath) var(--breath-ease) infinite alternate;
}
/* Cool, upper left. NOT --desk: in a warm register --desk is a deeper tan and
   the two blooms come out the same temperature, which is a wash rather than air.
   A cool bloom is the part of the room the sun is not reaching — the register's
   own shadow — so it is mixed from the paper toward the ink. Derived, so every
   register gets its own and none of them are invented. */
.hl-room::before{
  /* Opposite the sun — the side of the room the light is not on. */
  top:calc(-10% - var(--light-y, .45) * 22%);
  left:calc(-12% + var(--light-x, 0) * 26%);
  background:radial-gradient(circle at 30% 30%,
    color-mix(in oklab, var(--paper), var(--ink) 26%) 0%, transparent 62%);
}
/* Warm, lower right — the register's own gold, and the only place
   gold is ever spread across an area rather than marked with. */
/* Warm, lower right. The gold is pulled well back from full strength — at full
   it reads as a gradient someone applied, not as light in a room. Air is almost
   nothing; you should only notice it is gone. */
.hl-room::after{
  /* Where the sun actually is: east in the morning, west by evening. */
  bottom:calc(-14% - var(--light-y, .45) * 20%);
  right:calc(-10% - var(--light-x, 0) * 30%);
  background:radial-gradient(circle at 70% 70%,
    color-mix(in oklab, var(--bloom-warm, var(--gold)), var(--paper) 42%) 0%, transparent 62%);
  animation-delay:calc(var(--breath) / -2);   /* out of phase, like two lungs */
}

@keyframes hl-breathe{
  from{ transform:scale(1) translate(0,0); }
  to  { transform:scale(var(--bloom-drift)) translate(2%,2%); }
}

/* Paper noise over the room. Subliminal — not seen, only felt. */
.hl-room > .hl-grain,
.hl-grain{
  position:absolute; inset:0; z-index:-1; pointer-events:none;
  background-image:var(--grain-url);
  background-size:256px 256px;
  opacity:var(--grain-opacity);
  mix-blend-mode:var(--grain-blend);
}

/* ── glass ────────────────────────────────────────────────────
   Room furniture only: a register switch, a rail, a bar. Never a
   card, never a plate, never anything a person reads. */
.hl-glass{
  background:color-mix(in srgb, var(--paper) 62%, transparent);
  -webkit-backdrop-filter:blur(var(--glass-blur)) saturate(1.15);
  backdrop-filter:blur(var(--glass-blur)) saturate(1.15);
  border:1px solid var(--rule);
}
.hl-hush{
  -webkit-backdrop-filter:blur(var(--hush-blur));
  backdrop-filter:blur(var(--hush-blur));
}

/* ── bloom ────────────────────────────────────────────────────
   The live thing on the surface, lit rather than filled. One per
   surface, used like a full stop. */
.hl-bloom{ box-shadow:var(--glow-gold); }

/* A body that never asked to breathe should not be made to. */
@media (prefers-reduced-motion:reduce){
  .hl-room::before,
  .hl-room::after{ animation:none; }
}

/* Print is a printed thing. The room does not come with it. */
@media print{
  .hl-room::before,
  .hl-room::after,
  .hl-grain{ display:none !important; }
}


:root{
  /* Set by sky.js, or by the app from WeatherKit. These defaults are
     a clear day with the sun halfway up — the light a surface should
     have when nothing is known. */
  --sun:0.5 /* @kind other */;          /* 0 below horizon … 1 overhead */
  --sky-diffuse:1 /* @kind other */;    /* 1 clear … 3 fog */
  --sky-strength:1 /* @kind other */;   /* 1 clear … .35 storm */
  --sky-cool:0 /* @kind other */;       /* 0 warm … 1 cold */

  /* Low sun throws long and warm; high sun is short and white. The
     blooms grow as the sun drops, the way a shadow does. */
  /* Larger than the viewport on purpose: two visible circles read as decoration,
     a fall-off across the whole surface reads as a room with a window in it. */
  --bloom-size:calc(min(94vw, 1080px) * (1.35 - (var(--sun) * 0.35)) * var(--sky-diffuse));
  --bloom-blur:calc(60px * var(--sky-diffuse));
  --bloom-opacity:calc(0.34 * var(--sky-strength));

  /* Weather slows the room. Rain breathes longer than a clear noon. */
  /* Clear weather is exactly fifteen seconds — resting respiration. Anything
     between you and the sun lengthens it; fog breathes half again as slow. */
  --breath:calc(15s * (0.75 + (var(--sky-diffuse) * 0.25)));

  /* The warm bloom cools as the sky closes over. */
  --bloom-warm:color-mix(in oklab, var(--gold), var(--desk) calc(var(--sky-cool) * 55%));
}

html[data-register="vespers"],
html[data-register="midnight"],
html[data-register="nocturne"]{
  --bloom-opacity:calc(0.16 * var(--sky-strength));
}

/* The warm bloom takes the weather — mixed back toward the paper for the same
   reason it is in atmosphere.css: light in a room is almost nothing. */
.hl-room::after{
  background:radial-gradient(circle at 70% 70%,
    color-mix(in oklab, var(--bloom-warm), var(--paper) 42%) 0%, transparent 62%);
}

/* ── what each sky does ──────────────────────────────────────
   Set on the root by sky.js, or by an app that knows better. */
html[data-sky="clear"]     { --sky-diffuse:1;    --sky-strength:1;    --sky-cool:0;   }
html[data-sky="fair"]      { --sky-diffuse:1.25; --sky-strength:.92;  --sky-cool:.08; }
html[data-sky="overcast"]  { --sky-diffuse:1.9;  --sky-strength:.66;  --sky-cool:.42; }
html[data-sky="rain"]      { --sky-diffuse:2.1;  --sky-strength:.52;  --sky-cool:.62; }
html[data-sky="storm"]     { --sky-diffuse:2.4;  --sky-strength:.38;  --sky-cool:.74; }
html[data-sky="fog"]       { --sky-diffuse:3;    --sky-strength:.58;  --sky-cool:.5;  }
/* Snow is the one weather that makes the light *stronger* — the
   ground becomes a second sky and throws everything back up. */
html[data-sky="snow"]      { --sky-diffuse:2.2;  --sky-strength:.86;  --sky-cool:.68; }

/* ── the quality of the light on photography ─────────────────
   Each register already grades its plates. The sky grades the
   grade: an overcast February plate loses the contrast a clear
   June one keeps. Applied on top of --image-filter, never instead
   of it, and never inline on an <img> — that doubles it. */
html[data-sky]:not([data-sky="clear"]) img{
  filter:var(--image-filter, none)
         saturate(calc(1 - (var(--sky-cool) * 0.22)))
         contrast(calc(1 - (var(--sky-cool) * 0.10)))
         brightness(calc(0.97 + (var(--sun) * 0.06)));
}

/* Nothing in here animates on its own; it only changes what the
   room's own breath is made of. A surface that cannot move still
   gets the colour and the strength. */
@media (prefers-reduced-motion:reduce){
  :root{ --breath:15s; }
}
