/* phi-lam.com design system.
   Everything sits on an 8pt grid: padding, gaps, margins, sizes, radii and line-heights are multiples of 8.
   The only exceptions are 1-2px strokes and the hero light leak. The site is dark only. */

:root{
  /* color */
  --ground: #0D0D0C;          /* page background, every section */
  --surface: #171615;         /* cards, code blocks, mono label chips */
  --surface-raised: #262422;  /* label chips that sit on a Surface card */
  --line: #2A2826;            /* hairline dividers, nav chip hover */
  --text: #F2EFE9;
  --secondary: #B9B5AD;       /* body text on cards, descriptions, chip text */
  --tertiary: #9C978E;        /* minor labels */
  --highlight: #4169E1;       /* PLACEHOLDER royal blue: change it here and it updates everywhere */

  /* type */
  --sans: 'Instrument Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --serif: 'Instrument Serif', Georgia, 'Times New Roman', serif;
  --mono: 'Fragment Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  /* page gutters and section padding (shrink on phones, below) */
  --gutter: 32px;
  --section: 128px;

  color-scheme: dark;
}
@media (max-width: 640px){
  :root{ --gutter: 16px; --section: 64px; }
}

*, *::before, *::after{ box-sizing: border-box; }

/* On desktop the whole site is drawn at 90%, the size Phi preferred. Everything is
   still designed on the 8pt grid; it simply renders at nine tenths the size. Phones and tablets stay at full size. */
@media (min-width: 1024px){ html{ zoom: 0.9; } }
html{ background: var(--ground); -webkit-text-size-adjust: 100%; }
body{
  margin: 0;
  background: var(--ground);
  color: var(--text);
  font-family: var(--sans);
  font-size: 20px;
  line-height: 32px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img, video, svg{ display: block; max-width: 100%; }
a{ color: inherit; }
h1, h2, h3, p, figure, pre{ margin: 0; }

/* visible focus for keyboard users, never for mouse clicks */
:focus-visible{ outline: 2px solid var(--highlight); outline-offset: 4px; border-radius: 8px; }

.skip-link{
  position: absolute; left: var(--gutter); top: -64px; z-index: 10;
  font-family: var(--mono); font-size: 14px; line-height: 24px;
  background: var(--surface); color: var(--text); padding: 12px 16px; border-radius: 8px; text-decoration: none;
}
.skip-link:focus{ top: 16px; }

/* ---------- type scale (size / line-height) ---------- */
.t-section{ font-size: 96px; line-height: 96px; font-weight: 400; letter-spacing: -0.03em; }   /* Digital, Analog, case-study titles */
.t-hero{ font-size: 64px; line-height: 72px; font-weight: 400; letter-spacing: -0.02em; text-wrap: pretty; }
.t-heading{ font-size: 48px; line-height: 56px; font-weight: 400; letter-spacing: -0.02em; }   /* page and case-study headings */
.t-title{ font-size: 32px; line-height: 40px; font-weight: 400; letter-spacing: -0.01em; }     /* card titles */
.t-sub{ font-size: 24px; line-height: 32px; }                                                  /* subheadings, About body */
.t-body{ font-size: 20px; line-height: 32px; }
.t-mono{ font-family: var(--mono); font-size: 14px; line-height: 24px; font-weight: 400; }
.t-mono-sm{ font-family: var(--mono); font-size: 12px; line-height: 24px; font-weight: 400; }
.secondary{ color: var(--secondary); }
@media (max-width: 640px){
  .t-section{ font-size: 56px; line-height: 56px; }
  .t-hero{ font-size: 32px; line-height: 40px; }
}

/* the italic accent: the keywords in the hero sentence and the contact links */
.kw{ font-family: var(--serif); font-style: italic; font-weight: 400; }

/* ---------- underlined keywords and links ----------
   At rest the text keeps its color and only the underline is blue. On hover it fills with blue. */
.ul{
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.14em;
  text-decoration-color: var(--highlight);
  border-radius: 4px;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  transition: background-color .2s ease, color .2s ease, text-decoration-color .2s ease;
}
.ul:hover{ background-color: var(--highlight); color: var(--text); text-decoration-color: transparent; }
/* the italic keywords and contact links turn blue on hover instead of filling */
.kw.ul:hover{ background-color: transparent; color: var(--highlight); text-decoration-color: var(--highlight); }

/* selecting text (click and drag) uses the royal blue */
::selection{ background-color: var(--highlight); color: var(--text); }

/* ---------- mono label chips ----------
   Every piece of Fragment Mono utility text sits on a small card. Chips inside a Surface card use .chip-raised. */
.chip{
  display: inline-block;
  align-self: flex-start;
  background: var(--surface);
  color: var(--secondary);
  border-radius: 8px;
  padding: 0 8px;
  line-height: 24px;
  transition: background-color .2s ease;
}
.chip-raised{ background: var(--surface-raised); }

/* ---------- layout ---------- */
.px{ padding-left: var(--gutter); padding-right: var(--gutter); }
.sec{ padding-top: var(--section); padding-bottom: var(--section); }

/* header: name on the left, nav on the right. Each link keeps a 48px tap height (12px around the 24px chip). */
.site-header{
  position: relative;
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: center;
  gap: 0 32px;
  padding-top: 16px; padding-bottom: 16px;
}
.site-header nav{ display: flex; flex-wrap: wrap; gap: 0 24px; }
@media (max-width: 640px){ .site-header nav{ gap: 0 16px; } }
.nav-link{ display: inline-block; padding: 12px 0; text-decoration: none; color: var(--text); }
.nav-link .chip{ color: var(--text); }
.nav-link:hover .chip{ background: var(--line); }
.nav-link:focus-visible{ outline: none; }
.nav-link:focus-visible .chip{ outline: 2px solid var(--highlight); outline-offset: 2px; }

/* section title with its description to the right */
.sec-head{
  display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline;
  gap: 16px 48px;
  margin-bottom: 48px;
}
.sec-head p{ max-width: 464px; color: var(--secondary); }

.grid{ display: grid; gap: 16px; }
.grid-digital{ grid-template-columns: repeat(auto-fit, minmax(min(100%, 352px), 1fr)); }
.grid-analog{ grid-template-columns: repeat(auto-fit, minmax(min(100%, 384px), 1fr)); }

.card{ background: var(--surface); border-radius: 32px; color: var(--text); text-decoration: none; }

.site-footer{ padding-top: 96px; padding-bottom: 40px; }

/* ---------- iPhone bezel ----------
   iphone-frame.png is 1470 x 3000 with a transparent screen opening at x 75 to 1394, y 66 to 2933.
   The video sits behind the frame, cut to the same shape. */
.iphone, .watch{ position: relative; width: 100%; }
.iphone .frame, .watch .frame{ position: relative; z-index: 2; width: 100%; height: auto; pointer-events: none; }
.iphone .screen, .watch .screen{ position: absolute; z-index: 1; overflow: hidden; background: #000; }
.iphone .screen{
  position: absolute; z-index: 1;
  left: 4.082%; right: 4.082%; top: 1.7%; bottom: 1.7%;
  border-radius: 15.93% / 7.42%;
  overflow: hidden;
  background: #000;
}
.iphone .screen video, .watch .screen video{ width: 100%; height: 100%; object-fit: cover; }
/* landscape: iphone-frame-landscape.png is the same frame turned on its side (3000 x 1470) */
.iphone.landscape .screen{ left: 1.7%; right: 1.7%; top: 4.082%; bottom: 4.082%; border-radius: 7.42% / 15.93%; }
/* Apple Watch Ultra: apple-watch-frame.png is 600 x 940 with a 410 x 502 screen opening */
.watch .screen{ left: 15.17%; right: 15.17%; top: 22.87%; bottom: 22.87%; border-radius: 0; }

/* ---------- Digital project cards: 720px tall, the iPhone cropped at the bottom ---------- */
.project-card{
  display: flex; flex-direction: column;
  height: 720px;
  padding: 32px 32px 0;
  overflow: hidden;
}
.project-card .desc{ margin-top: 8px; color: var(--secondary); }
.project-card .phone{
  flex: 1 1 0; min-height: 0;
  width: min(304px, 100%);
  margin: 0 auto;
  padding-top: 40px;
  transition: transform .5s cubic-bezier(.2,.7,.2,1);
}
.project-card{ position: relative; }
.project-card:hover .phone{ transform: translateY(-8px); }
/* the title link covers the whole card, so clicking anywhere opens the case study; the bulb sits above it */
.card-title{ margin: 0; padding-right: 56px; }  /* a card with no case study yet: plain title, not a link */
.card-link{ color: inherit; text-decoration: none; padding-right: 56px; }  /* keeps the title clear of the bulb */
.card-link::after{ content: ""; position: absolute; inset: 0; z-index: 1; border-radius: 32px; }
.card-link:focus-visible{ outline: none; }
.card-link:focus-visible::after{ outline: 2px solid var(--highlight); outline-offset: 4px; }

/* light switch for each product: lights only that phone, which changes appearance the way iOS does,
   a 0.35s ease-in-out cross-dissolve of the whole screen */
.bulb{
  position: absolute; z-index: 2; top: 24px; right: 24px;
  width: 48px; height: 48px; padding: 0; border: 0; border-radius: 16px;
  display: flex; align-items: center; justify-content: center;
  background: transparent; color: var(--secondary); cursor: pointer;
  transition: background-color .2s ease;
}
.bulb:hover{ background: var(--surface-raised); }
.bulb svg{ fill: transparent; stroke: currentColor; stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; overflow: visible;
  transition: color .35s ease-in-out, fill .35s ease-in-out, filter .35s ease-in-out; }
.bulb[aria-pressed="true"]{ color: var(--highlight); }
.bulb[aria-pressed="true"] svg{ filter: drop-shadow(0 0 8px var(--highlight)); }
.bulb[aria-pressed="true"] .glass{ fill: var(--highlight); }
.bulb .glass{ transition: fill .35s ease-in-out; }
.screen .vid-light{ position: absolute; inset: 0; opacity: 0; transition: opacity .35s ease-in-out; }
.screen.is-light .vid-light{ opacity: 1; }

/* ---------- Analog cards ---------- */
.analog-card{ display: flex; flex-direction: column; gap: 32px; padding: 32px; }
.analog-card .stage{ height: 416px; }   /* room for photos, coming later */
.analog-card .meta{ display: flex; flex-direction: column; gap: 8px; }
.analog-card .meta .desc{ color: var(--secondary); }

/* Motion: the whole card is a field of spectral light rushing toward you (assets/motion.js draws it). A soft shade
   at the bottom keeps the text clear of the brightest light. Without WebGL the card stays dark. */
.motion-card{ position: relative; overflow: hidden; isolation: isolate; }
.motion-card .lightfield{ position: absolute; inset: 0; z-index: -1; background: #000; }
.motion-card .lightfield::after{ content: ""; position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,.6), rgba(0,0,0,0) 32%); }
.lightfield-canvas{ position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.motion-card .meta .desc{ color: var(--text); }

/* Motorsport: the photos fill the card in a light black and white (baked into the images) and crossfade, each held
   for about 4 seconds. A soft shade at the bottom keeps the text readable. Reduced motion shows the first photo only. */
.photo-card{ position: relative; overflow: hidden; isolation: isolate; }
.photo-card .photos{ position: absolute; inset: 0; z-index: -1; background: #000; }
.photo-card .photos::after{
  content: ""; position: absolute; inset: 0; z-index: 1;
  background:
    radial-gradient(ellipse 85% 80% at 50% 45%, rgba(0,0,0,0) 55%, rgba(0,0,0,.38) 100%),   /* a slight vignette, like an old lens */
    linear-gradient(to top, rgba(0,0,0,.7), rgba(0,0,0,0) 40%);
}
.photo-card .photos img{ position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; animation: photo-cycle 15s infinite; }
.photo-card .photos img:nth-child(1){ animation-delay: 0s; }
.photo-card .photos img:nth-child(2){ animation-delay: 5s; }
.photo-card .photos img:nth-child(3){ animation-delay: 10s; }
@keyframes photo-cycle{ 0%{ opacity: 0; } 8%{ opacity: 1; } 33%{ opacity: 1; } 41%{ opacity: 0; } 100%{ opacity: 0; } }
/* film grain over the photos: fine noise that jumps each frame (24 a second), blended so it reads in the mid-tones */
.photo-card .photos::before{
  content: ""; position: absolute; inset: -50%; z-index: 1; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='1.8' intercept='-0.4'/%3E%3CfeFuncG type='linear' slope='1.8' intercept='-0.4'/%3E%3CfeFuncB type='linear' slope='1.8' intercept='-0.4'/%3E%3CfeFuncA type='linear' slope='0' intercept='0.6'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  mix-blend-mode: overlay; opacity: .9;
  animation: grain-jump .5s steps(12) infinite;
}
@keyframes grain-jump{
  0%{ transform: translate(0, 0); } 10%{ transform: translate(-7%, 4%); } 20%{ transform: translate(5%, -9%); }
  30%{ transform: translate(-3%, 11%); } 40%{ transform: translate(9%, 2%); } 50%{ transform: translate(-10%, -5%); }
  60%{ transform: translate(4%, 8%); } 70%{ transform: translate(-6%, -11%); } 80%{ transform: translate(11%, -3%); }
  90%{ transform: translate(-2%, 6%); } 100%{ transform: translate(0, 0); }
}
.photo-card .meta .desc{ color: var(--text); }

/* ---------- privacy policies ----------
   They use the case-study layout (.cs-main, .cs-title, .cs-col), so the hierarchy is the same everywhere. */
.cs-col p a{ color: var(--text); }

/* ---------- motion preferences ---------- */
@media (prefers-reduced-motion: reduce){
  .photo-card .photos img{ animation: none; }
  .photo-card .photos img:first-child{ opacity: 1; }
  .photo-card .photos::before{ animation: none; }
  .project-card .phone, .ul, .chip{ transition: none; }
}

/* ---------- home hero ----------
   The light leak sits behind the header and the sentence. It is drawn by a WebGL shader (assets/hero-leak.js)
   on a canvas inside .hero-leak. Without WebGL, .hero-leak shows a still frame captured from the shader instead.
   The shader feathers the bottom itself, so the glow fades into Ground with no edge where Digital starts. */
.hero{ position: relative; overflow: hidden; min-height: 768px; display: flex; flex-direction: column; }
.hero-leak{
  position: absolute; inset: 0; pointer-events: none;
  background: var(--ground) url(/assets/hero-fallback.jpg) center bottom / cover no-repeat;
}
.hero-leak.is-live{ background: none; }
.liquid-canvas{ position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.hero-copy{ position: relative; padding-top: 96px; padding-bottom: 128px; max-width: 1408px; }
.hero-copy .name{ margin-top: 32px; color: var(--secondary); }
.hero-copy .name strong{ font-weight: 500; color: var(--text); }

/* ---------- about and contact ---------- */
.about{ padding-top: var(--section); }
.about-grid{ display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 416px), 1fr)); gap: 48px 64px; max-width: 1408px; }
.about-grid > div{ display: flex; flex-direction: column; gap: 24px; }
.about-body{ display: flex; flex-direction: column; gap: 24px; max-width: 560px; }
.contact-links{ display: flex; flex-wrap: wrap; gap: 0 32px; }
.contact-links a{ padding: 8px 0; }

/* ---------- case studies ---------- */
.cs-main{ display: flex; flex-direction: column; gap: 64px; padding-top: 96px; padding-bottom: 128px; }
.cs-wide{ width: 100%; max-width: 1024px; margin-left: auto; margin-right: auto; }
/* the title and the reading column form one centered block. The title is the one exception that is
   center-aligned; the text below is left-aligned. The column is 728px so "Arsis Metronome" spans it exactly. */
.cs-title{ width: 100%; max-width: 728px; margin-left: auto; margin-right: auto; text-align: center; }
/* a title too long for the column wraps into evenly balanced, centered lines */
.cs-title h1{ text-wrap: balance; }

/* the reading column */
.cs-col{ width: 100%; max-width: 728px; margin-left: auto; margin-right: auto; display: flex; flex-direction: column; gap: 24px; }
.cs-col p{ font-size: 20px; line-height: 32px; }
.cs-col h2{ margin-top: 32px; }
.cs-col h3{ font-size: 24px; line-height: 32px; font-weight: 500; }

/* each demo sits straight on the page, one standard gap (64px, from .cs-main) from the text around it, with its
   bulb right beside the device */
.media{ position: relative; display: flex; justify-content: center; padding: 0; }
.media .device-wrap{ position: relative; width: fit-content; max-width: 100%; min-width: 0; }
.media .bulb{ top: 0; right: auto; left: calc(100% + 16px); }
/* Every device is drawn at one scale: --phone is the width of a portrait iPhone, and everything else follows from it.
   A landscape iPhone is the same phone turned (as wide as the portrait one is tall), and every Apple Watch is the
   size it is next to the iPhone in the pair. */
.cs-main{ --phone: 304px; }
.media .dev-portrait{ width: var(--phone); max-width: 100%; }
.media .dev-landscape{ width: calc(var(--phone) * 3000 / 1470); max-width: 100%; }
.media .dev-watch{ width: calc(var(--phone) / 0.7778 * 0.4444); max-width: 100%; }
/* iPhone with the Watch in front of it, lower right */
.media .dev-pair{ position: relative; width: calc(var(--phone) / 0.7778); max-width: 100%; }
.media .dev-pair .iphone{ width: 77.78%; }
.media .dev-pair .watch{ position: absolute; z-index: 3; left: 55.56%; bottom: 0; width: 44.44%; }
/* when there's no room beside the device, the bulb sits just above its top-right corner */
@media (max-width: 1040px){
  .media{ padding-top: 64px; }
  .media .bulb{ top: -64px; left: auto; right: 0; }
}
@media (max-width: 640px){
  .cs-main{ --phone: 256px; }
}

/* code */
.code{
  margin: 0; background: var(--surface); border-radius: 24px; padding: 24px; overflow-x: auto;
  font-family: var(--mono); font-size: 14px; line-height: 24px; color: #E6E2DA;
}
.code pre{ margin: 0; white-space: pre; font: inherit; }

/* App Store reviews: a wide grid running the full width of the page. The rating sits in a large card of its own,
   two rows tall, and the six reviews fill the rest. */
.reviews-wide .reviews{ display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 16px; }
.reviews-wide .card{ border-radius: 24px; }
.reviews-wide .rating{
  grid-row: span 2;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px;
  padding: 32px; text-align: center;
}
.reviews-wide .rating h2{ font-weight: 400; }
.reviews-wide .rating .score{ display: block; font-size: 128px; line-height: 128px; letter-spacing: -0.04em; }
.reviews-wide .rating .where{ display: block; margin-top: 8px; font-size: 20px; line-height: 32px; color: var(--secondary); }
.reviews-wide .rating .count{ font-family: var(--mono); font-size: 14px; line-height: 24px; color: var(--secondary); }
.reviews-wide .stars{ color: var(--highlight); letter-spacing: 2px; white-space: nowrap; }
.reviews-wide .rating .stars{ font-size: 24px; line-height: 32px; letter-spacing: 4px; }
.reviews-wide .review{ display: flex; flex-direction: column; gap: 8px; padding: 24px; }
.reviews-wide .review h3{ font-size: 20px; line-height: 32px; font-weight: 500; }
.reviews-wide .review h3 .stars{ font-size: 16px; margin-left: 8px; }
.reviews-wide .review p{ font-size: 20px; line-height: 32px; color: var(--text); }
@media (max-width: 1100px){
  .reviews-wide .reviews{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .reviews-wide .rating{ grid-row: auto; grid-column: 1 / -1; }
}
@media (max-width: 640px){
  .reviews-wide .reviews{ grid-template-columns: minmax(0, 1fr); }
  .reviews-wide .rating .score{ font-size: 96px; line-height: 96px; }
}

/* code colors from Xcode's Default (Dark) theme */
.code{ color: #DFDFE0; }
.code .c{ color: #7F8C98; }                      /* comments */
.code .k{ color: #FF7AB2; font-weight: 500; }    /* keywords */
.code .n{ color: #D9C97C; }                      /* numbers */
.code .t{ color: #DABAFF; }                      /* types */
.code .f{ color: #B281EB; }                      /* functions and methods */
.code .p{ color: #DFDFE0; }                      /* punctuation */

