/* =========================================================================
   DORMMATE PROJECT PAGE — page-specific styles.
   Reuses tokens, .btn and .stack-badge from ../style.css; everything here is
   unique to this page: hero, info cards, the Material You scroll-color
   section, the iOS sticky-scroll section, the built-with grid, the
   scroll-spy "inside the app" tabs, and the screenshot carousel.

   Layout below: cross-cutting utilities/overrides first (this page's base
   background + overflow fix, the shared readability-veil pattern, and the
   tokens that personalize the vendored "Inside the app" widget system),
   then one section per part of the page, top to bottom, in the order
   they appear on screen.
   ========================================================================= */

/* ============================================================
   UTILITIES — cross-cutting, used by more than one section below
   ============================================================ */

/* Horizontal-overflow containment lives on main.dm-main (see below), NOT on
   html/.dm-body. Any overflow value other than `visible` on an ancestor of a
   position:sticky element turns that ancestor into the element's scroll
   container — which, on Safari/iOS, silently kills the sticky .dm-top bar
   (Chromium is more forgiving, so it looked fine there). Keeping the top of
   the tree clip-free lets .dm-top stick against the viewport on every engine;
   .dm-main still clips the page's inner content so nothing scrolls sideways.

   scrollbar-gutter: stable reserves the scrollbar's width permanently.
   Without it, opening/closing a card popup (vendor/morph-card.js's
   lockScroll/unlockScroll) toggles body to position:fixed and back, which
   removes and restores the scrollbar and reflows the whole page a few px
   sideways right as a popup closes. */
html:has(> .dm-body) { overflow-x: visible; scrollbar-gutter: stable; }
.dm-body { background: var(--paper); overflow-x: visible; }

/* ---------- Cross-document transition from the portfolio ----------
   The portfolio's DormMate card links here as a full navigation. Opting in on
   both documents lets the browser morph across it: the card's cover image and
   this page's hero logo share the name `dormmate-cover`, so one melts into the
   other (and back again when the ← link returns to the portfolio). Browsers
   without cross-document View Transitions just navigate normally. */
@view-transition { navigation: auto; }
/* The portfolio card now morphs its front phone into THIS hero's phone screen
   (device → device), so the shared name lives on the hero phone's screenshot. */
.dm-hero__phone.dm-phone--ios-phone .dm-phone__screen img { view-transition-name: dormmate-cover; }
::view-transition-old(dormmate-cover),
::view-transition-new(dormmate-cover) { animation-duration: 0.5s; }

/* ---------- Readability veil under floating text ----------
   Same technique used site-wide (see root style.css): a soft feathered
   paper wash sits under text that floats directly over the dot-field
   background, so the dots don't visually collide with the glyphs.
   Contained + masked so it never bleeds into neighbouring elements.

   Applied to every text block on this page that sits directly on the bare
   background. Deliberately NOT applied to:
   - .dm-platform__panel text (sits on the panel's own tinted/glass surface)
   - .dm-info__card / .dm-built__item / .dm-toggle (already have their own
     veil+blur card background — adding this too would double up)
   - the "Inside the app" live cards and their popups (each has its own
     opaque/coloured background by design, matching the real app screens)
   New text blocks added straight onto the bare background should be added
   to this list rather than left bare. */
.dm-hero__eyebrow,
.dm-hero__title,
.dm-hero__tagline,
.dm-hero__spec,
.dm-overview__label,
.dm-overview__lead,
.dm-overview__text,
.dm-platform__intro,
.dm-bom,
.dm-carousel__caption,
.dm-footer,
.dm-inside__intro,
.dm-inside__group-head > div,
.dm-inside__text {
  position: relative;
}
.dm-hero__eyebrow::before,
.dm-hero__title::before,
.dm-hero__tagline::before,
.dm-hero__spec::before,
.dm-overview__label::before,
.dm-overview__lead::before,
.dm-overview__text::before,
.dm-platform__intro::before,
.dm-bom::before,
.dm-carousel__caption::before,
.dm-footer::before,
.dm-inside__intro::before,
.dm-inside__group-head > div::before,
.dm-inside__text::before {
  content: "";
  position: absolute;
  inset: -0.6rem -0.9rem;
  z-index: -1;
  background: var(--veil);
  pointer-events: none;
  -webkit-mask-image: radial-gradient(115% 130% at 50% 50%, #000 52%, transparent 100%);
  mask-image: radial-gradient(115% 130% at 50% 50%, #000 52%, transparent 100%);
}
/* Same exception as the portfolio's .site-footer (see root style.css): the
   footer bar already spans edge to edge and makes its own gutter with padding,
   so the wash needs no horizontal breathing room. With the default -0.9rem it
   overhangs the viewport by 14px, and since this page deliberately has no
   overflow-x clip anywhere (see the note at the top of this file) those 14px
   became a real horizontal scrollbar on the whole document. */
.dm-footer::before { inset: -0.6rem 0; }

/* ---------- Personalize the vendored widget-card system ----------
   The "Inside the app" cards reuse DormMate's own CSS (vendor/dormmate-widgets.css)
   almost verbatim for authenticity, but a couple of tokens are retuned here so
   the section feels like part of THIS portfolio rather than a copy of the
   reference build: the accent ties to --forest instead of DormMate's own
   green, and the popup backdrop matches the site's veil/blur language. */
:root {
  --dm-primary: var(--forest);
  --dm-on-primary: var(--paper);
}
@media (prefers-color-scheme: dark) {
  :root {
    --dm-primary: var(--forest);
    --dm-on-primary: var(--paper);
  }
}

.morph-overlay {
  background: rgba(var(--forest-rgb), 0.55);
}
.morph-overlay--active {
  backdrop-filter: blur(16px) saturate(1.1);
  -webkit-backdrop-filter: blur(16px) saturate(1.1);
}

/* ============================================================
   PAGE SECTIONS — one per part of the page, top to bottom
   ============================================================ */

.dm-top {
  position: sticky;
  top: 0;
  z-index: 30;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 1.25rem var(--gutter);
  background: var(--veil);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.dm-back {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--forest);
}
.dm-top-github { color: var(--graphite); font-family: var(--font-mono); font-size: 0.78rem; }
.dm-top-github:hover { color: var(--forest); }

/* No overflow-x: clip here. It used to catch stray horizontal overshoot, but a
   clip on this element (an ancestor of every sticky bar on the page) turns it
   into their scroll container and kills position:sticky on Safari/iOS — both
   the top bar and the "Inside the app" tab strip. The page is sized to fit
   instead, so nothing needs clipping. */
main.dm-main { max-width: 1180px; margin: 0 auto; padding: 0 var(--gutter); }

.dm-section-title {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 100;
  font-weight: 700;
  font-size: clamp(1.6rem, 3vw, 2.2rem);
}

/* ---------- Reusable phone frame (precise crops, measured from the real
   assets — each combo has its own screen-cutout percentages) --------------- */
.dm-phone {
  position: relative;
  width: 100%;
  margin-inline: auto;
}
.dm-phone__screen {
  position: absolute;
  overflow: hidden;
  background: var(--forest-deep);
}
.dm-phone__screen img,
.dm-phone__screen video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.dm-phone__frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* iOS portrait phone: 1470x3000 asset, screen at 5.10%/2.2%/89.8%/95.6% */
.dm-phone--ios-phone { aspect-ratio: 1470 / 3000; }
.dm-phone--ios-phone .dm-phone__screen {
  left: 5.10%; top: 2.2%; width: 89.8%; height: 95.6%;
  border-radius: 9% / 4.4%;
}

/* Android portrait phone: 771x1628 vector frame, screen at 4.05%/1.61%/91.44%/96.31% */
.dm-phone--android-phone { aspect-ratio: 771 / 1628; }
.dm-phone--android-phone .dm-phone__screen {
  left: 4.05%; top: 1.61%; width: 91.44%; height: 96.31%;
  border-radius: 7% / 3.3%;
}

/* iOS tablet (landscape): 3000x2300 asset, screen at 4.13%/5.13%/91.73%/89.74% */
.dm-phone--ios-tablet { aspect-ratio: 3000 / 2300; }
.dm-phone--ios-tablet .dm-phone__screen {
  left: 4.13%; top: 5.13%; width: 91.73%; height: 89.74%;
  border-radius: 4% / 5%;
}

/* Android tablet (landscape): 1427x935 vector frame, screen at 3.51%/5.35%/92.99%/89.20% */
.dm-phone--android-tablet { aspect-ratio: 1427 / 935; }
.dm-phone--android-tablet .dm-phone__screen {
  left: 3.51%; top: 5.35%; width: 92.99%; height: 89.20%;
  border-radius: 4% / 6%;
}

/* iPad Windowed: a full desktop capture (Expo Go window on a Mac desktop),
   not a raw device screen — no bezel to overlay, the screenshot already
   carries its own window chrome, so it fills the box edge to edge. */
.dm-phone--ios-windowed { aspect-ratio: 3180 / 2384; }
.dm-phone--ios-windowed .dm-phone__screen {
  left: 0; top: 0; width: 100%; height: 100%;
  border-radius: 10px;
}
.dm-phone--ios-windowed .dm-phone__frame { display: none; }

/* ---------- Hero: editorial title block + a single leaning device ----------
   Deliberately asymmetric (text left, device right) so it reads as a spec
   sheet from THIS portfolio rather than the reference's centered logo flanked
   by two phones. */
.dm-hero {
  position: relative;
  padding: clamp(2.5rem, 7vh, 4.5rem) 0 clamp(3rem, 8vh, 5rem);
  min-height: 78vh;
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  align-items: center;
  gap: clamp(2rem, 5vw, 4rem);
  perspective: 1600px;
}
.dm-hero__intro { position: relative; z-index: 2; max-width: 34rem; }

.dm-hero__eyebrow {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--forest);
  width: fit-content;
}
.dm-hero__titlerow {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin: 1.2rem 0 0.4rem;
}
.dm-hero__logo { width: 72px; height: auto; flex-shrink: 0; }
.dm-hero__title {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 118;
  font-weight: 800;
  font-size: clamp(2.6rem, 6.5vw, 4.4rem);
  line-height: 0.95;
  letter-spacing: -0.01em;
  color: var(--forest);
}
.dm-hero__tagline {
  font-size: 1.08rem;
  color: var(--graphite);
  max-width: 44ch;
  margin-top: 0.5rem;
}

/* Spec block: mono key / value rows, like a drawing's title block */
.dm-hero__spec {
  margin-top: 1.6rem;
  display: grid;
  gap: 0.55rem;
  width: fit-content;
}
.dm-hero__spec > div {
  display: grid;
  grid-template-columns: 5.5rem 1fr;
  gap: 0.9rem;
  align-items: baseline;
}
.dm-hero__spec dt {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--forest);
}
.dm-hero__spec dd { font-size: 0.95rem; color: var(--ink); }

.dm-hero__cta { margin-top: 2rem; display: flex; gap: 1rem; flex-wrap: wrap; }

.dm-hero__mark {
  margin-top: 2.4rem;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  color: var(--graphite);
}
.dm-hero__mark-cross { color: var(--forest); font-size: 1.3rem; line-height: 1; }
.dm-hero__mark-label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.18em;
}

/* Device cluster: a lead iOS phone with an Android phone leaning behind it.
   The lead phone sits in normal flow so it drives the cluster's height (the
   back phone is absolute and only peeks out), which keeps the block from
   overflowing onto the text when the hero stacks on mobile. */
.dm-hero__device {
  position: relative;
  justify-self: center;
  width: min(100%, 300px);
  filter: drop-shadow(0 30px 55px rgba(var(--forest-rgb), 0.28));
}
.dm-hero__device .dm-phone--ios-phone {
  position: relative;
  z-index: 2;
  width: 66%;
  margin-left: 7%;
  transform: rotate(4deg);
}
.dm-hero__phone--back {
  position: absolute;
  right: 0;
  top: 5%;
  width: 62%;
  z-index: 1;
  opacity: 0.9;
  transform: rotate(-9deg);
}

/* ---------- Overview ----------
   No card: the opening statement floats straight on the page like the hero
   copy, a mono eyebrow keyed to a large lead paragraph. Readability comes from
   the shared feathered veil (::before, added to the veil lists above) rather
   than a boxed surface. */
.dm-overview {
  max-width: 68ch;
  padding: var(--section-gap) 0 clamp(2rem, 6vh, 4rem);
}
.dm-overview__label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--forest);
  margin-bottom: 1.1rem;
}
.dm-overview__lead {
  font-size: clamp(1.2rem, 2.2vw, 1.65rem);
  line-height: 1.5;
  font-weight: 400;
}
.dm-overview__lead strong { font-weight: 700; }
.dm-overview__text {
  margin-top: 1.1rem;
  color: var(--graphite);
  line-height: 1.6;
  max-width: 60ch;
}

/* ---------- Platforms: interactive diptych (replaces the two scroll sections)
   Two panels side by side. Android is a live palette picker (tap a swatch, the
   screenshot + the panel's tint follow — the "dynamic color" story told by
   interaction, not by scroll). iOS demonstrates Liquid Glass literally: the
   panel itself is a frosted glass card with a floating glass chip on top. --- */
.dm-platform { padding: 0 0 var(--section-gap); }
.dm-section-head { padding: 0; }
.dm-platform__intro {
  color: var(--graphite);
  max-width: 58ch;
  margin: -1.5rem 0 2.5rem;
}
.dm-platform__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
}
.dm-platform__panel {
  position: relative;
  border: 1px solid var(--line);
  border-radius: 24px;
  padding: 2rem clamp(1.5rem, 3vw, 2.25rem) 2.25rem;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  transition: background-color 0.5s var(--ease-soft), border-color 0.5s var(--ease-soft);
}
.dm-platform__head { position: relative; z-index: 1; }
.dm-platform__badges { display: flex; gap: 0.5rem; margin-bottom: 1.1rem; }
.dm-platform__title {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 105;
  font-weight: 800;
  font-size: clamp(1.6rem, 3vw, 2.2rem);
  line-height: 1.08;
}
.dm-platform__desc { color: var(--graphite); max-width: 40ch; margin-top: 0.9rem; }

.dm-platform__stage {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: center;
  margin-top: 2rem;
}
.dm-platform__phone { max-width: 240px; }

/* --- Android panel: tinted background + accent driven by [data-color] --- */
.dm-platform__accent { transition: color 0.5s var(--ease-soft); }
.dm-platform__accent-badge { transition: color 0.5s var(--ease-soft), border-color 0.5s var(--ease-soft); }
.dm-platform__phone img { transition: opacity 0.35s var(--ease-soft); }

.dm-platform__panel--android[data-color="green"]  { background: #eaf5ec; }
.dm-platform__panel--android[data-color="orange"] { background: #fbeee0; }
.dm-platform__panel--android[data-color="purple"] { background: #efe9f7; }
.dm-platform__panel--android[data-color="yellow"] { background: #faf3e0; }
.dm-platform__panel--android[data-color="green"]  .dm-platform__accent, .dm-platform__panel--android[data-color="green"]  .dm-platform__accent-badge { color: #386641; border-color: #386641; }
.dm-platform__panel--android[data-color="orange"] .dm-platform__accent, .dm-platform__panel--android[data-color="orange"] .dm-platform__accent-badge { color: #c96f19; border-color: #c96f19; }
.dm-platform__panel--android[data-color="purple"] .dm-platform__accent, .dm-platform__panel--android[data-color="purple"] .dm-platform__accent-badge { color: #6b4ba8; border-color: #6b4ba8; }
.dm-platform__panel--android[data-color="yellow"] .dm-platform__accent, .dm-platform__panel--android[data-color="yellow"] .dm-platform__accent-badge { color: #b8860b; border-color: #b8860b; }

.dm-platform__swatches { display: flex; gap: 0.7rem; justify-content: center; margin-top: 1.75rem; }
.dm-platform__swatch {
  width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid rgba(var(--ink-rgb), 0.14);
  cursor: pointer;
  transition: transform 0.22s var(--ease-soft), border-color 0.22s var(--ease-soft), box-shadow 0.22s var(--ease-soft);
}
.dm-platform__swatch[data-color="green"]  { background: #386641; }
.dm-platform__swatch[data-color="orange"] { background: #c96f19; }
.dm-platform__swatch[data-color="purple"] { background: #6b4ba8; }
.dm-platform__swatch[data-color="yellow"] { background: #b8860b; }
.dm-platform__swatch:hover { transform: scale(1.12); }
.dm-platform__swatch.is-active { transform: scale(1.28); border-color: rgba(var(--ink-rgb), 0.55); box-shadow: 0 0 0 3px var(--paper); }
.dm-platform__swatch:focus-visible { outline: 2px solid var(--forest); outline-offset: 3px; }

/* --- iOS panel: the Liquid Glass demo IS the panel's own frosted surface --- */
.dm-platform__panel--ios {
  background:
    radial-gradient(120% 90% at 15% 0%, rgba(116, 168, 146, 0.28), transparent 60%),
    radial-gradient(120% 90% at 100% 100%, rgba(27, 67, 50, 0.20), transparent 55%),
    var(--veil);
  backdrop-filter: blur(14px) saturate(1.15);
  -webkit-backdrop-filter: blur(14px) saturate(1.15);
}
.dm-platform__accent--ios {
  background: linear-gradient(120deg, #74a892, #1b4332, #74a892);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.dm-platform__accent-badge--ios { color: var(--forest); border-color: var(--forest); }

/* ---------- Built with: technology list ----------
   A genuine parts list, so item numbering carries real information. Not a card:
   transparent behind, hairline rules top/bottom and between rows, with the
   shared feathered veil (::before, added to the veil lists above) washed softly
   behind the whole list so it carries the same blur halo as every other
   floating element on the page. Rows keyed [no] · [icon] · [name + role] · [what]. */
.dm-built { padding: var(--section-gap) 0; }
.dm-bom {
  list-style: none;
  margin-top: 2rem;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.dm-bom__row {
  display: grid;
  grid-template-columns: auto auto minmax(8rem, 0.8fr) 1.7fr;
  gap: 1rem 1.5rem;
  align-items: center;
  padding: 1.5rem 0.75rem;
  transition: background-color 0.25s var(--ease-soft);
}
.dm-bom__row + .dm-bom__row { border-top: 1px solid var(--line); }
.dm-bom__row:hover { background: rgba(var(--forest-rgb), 0.04); }
.dm-bom__no {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  color: var(--forest);
  opacity: 0.55;
  transition: opacity 0.25s var(--ease-soft);
}
.dm-bom__row:hover .dm-bom__no { opacity: 1; }
.dm-bom__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.6rem;
  height: 2.6rem;
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--forest);
  font-size: 1.35rem;
  flex-shrink: 0;
}
.dm-bom__name {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 105;
  font-weight: 700;
  font-size: 1.15rem;
  line-height: 1.1;
}
.dm-bom__role {
  display: block;
  margin-top: 0.35rem;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--graphite);
}
.dm-bom__desc { color: var(--graphite); max-width: 54ch; }
.dm-bom__libs {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  max-width: 54ch;
}
.dm-bom__lib {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  padding: 0.3rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgba(var(--forest-rgb), 0.03);
  transition: border-color 0.25s var(--ease-soft), background-color 0.25s var(--ease-soft);
}
.dm-bom__row:hover .dm-bom__lib {
  border-color: rgba(var(--forest-rgb), 0.35);
  background: rgba(var(--forest-rgb), 0.06);
}
.dm-bom__lib-name {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--forest);
  line-height: 1.1;
}
.dm-bom__lib-role {
  font-size: 0.6rem;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--graphite);
}

/* ---------- Inside the app: scroll-spy tabs + floating components ---------- */
.dm-inside { padding: var(--section-gap) 0; }
.dm-inside__heading { margin-bottom: 0.75rem; }
.dm-inside__intro { color: var(--graphite); max-width: 64ch; margin-bottom: 2.5rem; }

.dm-inside__tabs {
  position: sticky;
  top: 4.5rem;
  z-index: 10;
  display: inline-flex;
  gap: 0.3rem;
  background: var(--veil);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.35rem;
  margin: 0 auto 3rem;
  box-shadow: 0 6px 20px rgba(var(--forest-rgb), 0.10);
}
.dm-inside__tab {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--font-mono);
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  padding: 0.62em 1.25em;
  border-radius: 999px;
  color: var(--graphite);
  transition: background-color 0.25s var(--ease-soft), color 0.25s var(--ease-soft);
}
.dm-inside__tab:hover { color: var(--forest); background: rgba(var(--forest-rgb), 0.08); }
.dm-inside__tab-icon { font-size: 1em; }
.dm-inside__tab.is-active {
  background: var(--forest);
  color: var(--paper);
  box-shadow: 0 3px 10px rgba(var(--forest-rgb), 0.35);
}
.dm-inside__tab.is-active:hover { background: var(--forest); color: var(--paper); }

.dm-inside { counter-reset: screen; }
.dm-inside__group { padding-top: 2.25rem; scroll-margin-top: 6rem; counter-increment: screen; }
.dm-inside__group-head {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  margin-bottom: 0.85rem;
}
.dm-inside__group-head::after {
  content: "SCREEN " counter(screen, decimal-leading-zero) " / 04";
  margin-left: auto;
  align-self: flex-end;
  position: relative;
  top: 0.3rem;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  color: var(--forest);
  white-space: nowrap;
  background: var(--veil);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border-radius: 6px;
  padding: 0.2em 0.4em;
}
.dm-inside__group-icon {
  font-size: 1.7rem;
  color: var(--paper);
  background: var(--forest);
  width: 3.2rem;
  height: 3.2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 14px;
  flex-shrink: 0;
  box-shadow: 0 4px 14px rgba(var(--forest-rgb), 0.30);
}
.dm-inside__group-head h3 { font-size: clamp(1.7rem, 4vw, 2.2rem); font-weight: 700; line-height: 1.1; }
.dm-inside__group-head p { color: var(--graphite); font-size: 0.98rem; margin-top: 0.25rem; }

.dm-inside__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(1.5rem, 5vw, 3.5rem);
  padding: 2.5rem 0;
  border-top: 1px solid var(--line);
}
.dm-inside__row--reverse .dm-inside__media { order: 2; }
.dm-inside__row--reverse .dm-inside__text   { order: 1; }

.dm-inside__media { display: flex; justify-content: center; }

.dm-inside__eyebrow {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--forest);
  margin-bottom: 0.5rem;
}
.dm-inside__text h4 { font-size: 1.5rem; font-weight: 700; margin-bottom: 0.7rem; }
.dm-inside__text p { color: var(--graphite); max-width: 42ch; }

/* ---------- Screenshot carousel ---------- */
.dm-carousel { padding: var(--section-gap) 0; text-align: center; }
.dm-carousel__toggles { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.75rem 1rem; margin: 1.5rem 0 2.5rem; }
.dm-toggle {
  position: relative;
  display: inline-flex;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.25rem;
  gap: 0.15rem;
  background: var(--veil);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
/* The active pill glides between buttons instead of the fill hard-cutting from
   one to the next. Its left/width are measured in JS from the active button so
   it stays exact as the button set changes (e.g. iPad-only option hides on
   Android). Sits behind the labels; each button lifts above it via z-index. */
.dm-toggle__thumb {
  position: absolute;
  top: 0.25rem;
  left: 0;
  width: 0;
  height: calc(100% - 0.5rem);
  border-radius: 999px;
  background: var(--forest);
  box-shadow: 0 4px 12px -4px rgba(var(--forest-rgb), 0.55);
  transition: left 0.42s var(--ease-out), width 0.42s var(--ease-out);
  pointer-events: none;
}
.dm-toggle__btn {
  position: relative;
  z-index: 1;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.5em 1.2em;
  border-radius: 999px;
  color: var(--graphite);
  transition: color 0.3s var(--ease-soft), transform 0.15s var(--ease-soft);
}
.dm-toggle__btn:not(.is-active):hover { color: var(--forest); }
.dm-toggle__btn:active { transform: scale(0.94); }
.dm-toggle__btn.is-active { color: var(--paper); }

.dm-carousel__stage {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(1rem, 4vw, 3rem);
}
.dm-carousel__arrow {
  width: 44px;
  height: 44px;
  flex-shrink: 0;
  border: 1px solid var(--line);
  border-radius: 50%;
  font-size: 1.3rem;
  color: var(--forest);
  background: var(--veil);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: background-color 0.2s var(--ease-soft), transform 0.2s var(--ease-soft);
}
.dm-carousel__arrow:hover { background: rgba(var(--forest-rgb), 0.1); transform: scale(1.06); }

.dm-carousel__phone-wrap { width: 100%; max-width: 300px; transition: max-width 0.45s var(--ease-out); }
.dm-carousel__phone-wrap.is-tablet { max-width: 520px; }
.dm-carousel__phone-wrap.is-windowed { max-width: 640px; }

/* Two stacked screen layers so one screenshot cross-fades into the next
   instead of popping. JS toggles which layer is on top by flipping opacity. */
.dm-carousel .dm-phone__screen img {
  position: absolute;
  inset: 0;
  transition: opacity 0.4s var(--ease-soft);
}
.dm-carousel .dm-phone__screen img[aria-hidden="true"] { opacity: 0; }

/* Changing device type is a bigger jump than flicking between screens, so the
   whole phone dips out (blur + shrink) and back in while the frame, aspect
   ratio and screenshot are swapped at the midpoint — a clean hand-off rather
   than the frame morphing through mismatched shapes. */
@keyframes dm-device-swap {
  0%   { opacity: 1; transform: scale(1)    translateY(0);   filter: blur(0); }
  42%  { opacity: 0; transform: scale(0.92) translateY(12px); filter: blur(5px); }
  58%  { opacity: 0; transform: scale(0.92) translateY(-12px); filter: blur(5px); }
  100% { opacity: 1; transform: scale(1)    translateY(0);   filter: blur(0); }
}
.dm-carousel__phone-wrap.is-switching .dm-phone {
  animation: dm-device-swap 0.5s var(--ease-soft);
}

.dm-carousel__caption {
  margin-top: 1.5rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--graphite);
}
.dm-carousel__dots {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.45rem;
  margin-top: 1.25rem;
  max-width: 500px;
  margin-inline: auto;
}
.dm-carousel__dots button {
  width: 8px; height: 8px; border-radius: 50%;
  background: rgba(var(--forest-rgb), 0.25);
  transition: background-color 0.2s var(--ease-soft), transform 0.2s var(--ease-soft);
}
.dm-carousel__dots button.is-active { background: var(--forest); transform: scale(1.3); }

/* ---------- Footer ---------- */
.dm-footer {
  padding: 2.5rem var(--gutter) 3rem;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.78rem;
  color: var(--graphite);
  border-top: 1px solid var(--line);
}
.dm-footer a { color: var(--forest); }

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
  .dm-hero {
    grid-template-columns: 1fr;
    text-align: center;
    min-height: 0;
    gap: 1rem;
  }
  .dm-hero__intro { max-width: none; }
  .dm-hero__eyebrow,
  .dm-hero__spec { margin-inline: auto; }
  .dm-hero__titlerow,
  .dm-hero__cta,
  .dm-hero__mark { justify-content: center; }
  .dm-hero__tagline { max-width: 52ch; margin-inline: auto; }
  .dm-hero__device { order: -1; width: min(80%, 260px); }
  .dm-platform__grid { grid-template-columns: 1fr; }
  .dm-platform__desc { margin-inline: auto; }
  .dm-bom__row { grid-template-columns: auto auto 1fr; row-gap: 0.8rem; }
  .dm-bom__desc { grid-column: 1 / -1; }
  .dm-inside__row,
  .dm-inside__row--reverse {
    grid-template-columns: 1fr;
    text-align: center;
  }
  .dm-inside__row .dm-inside__media,
  .dm-inside__row--reverse .dm-inside__media { order: 0; }
  .dm-inside__row .dm-inside__text,
  .dm-inside__row--reverse .dm-inside__text { order: 0; }
  .dm-inside__text p { margin-inline: auto; }
  /* Let the "SCREEN 01 / 04" caption (::after, nowrap, pushed right) drop to
     its own line instead of forcing the icon + heading row wider than the
     viewport. */
  .dm-inside__group-head { flex-wrap: wrap; }
  /* The in-app widget mockups are pixel-fixed phone screens (~320px). Clip
     their carrier so they never push the page sideways on sub-360px screens.
     .dm-inside__media is not an ancestor of the sticky tab strip, so this is
     safe for position:sticky. */
  .dm-inside__media { overflow-x: clip; min-width: 0; }
  /* Tab strip stays sticky on mobile too, tucked just under the top bar
     (~62px tall) as a slim floating pill with very small labels. */
  .dm-inside__tabs {
    position: sticky;
    top: 4rem;
    flex-wrap: wrap;
    justify-content: center;
    max-width: 100%;
    margin-bottom: 2rem;
  }
  .dm-inside__tab { font-size: 0.6rem; padding: 0.5em 0.8em; gap: 0.3rem; }
  .dm-inside__tab-icon { font-size: 0.95em; }
}

/* ---------- Narrow phones: keep the hero from overflowing sideways ----------
   "DormMate" is one unbreakable word sitting next to a fixed-size logo, so on
   sub-400px screens the pair is wider than the viewport. Shrink both (and cap
   the spec block) so the whole hero fits and the page never scrolls sideways. */
@media (max-width: 480px) {
  .dm-hero__titlerow { gap: 0.7rem; }
  .dm-hero__logo { width: 52px; }
  .dm-hero__title { font-size: clamp(2rem, 10vw, 3rem); }
  .dm-hero__spec { max-width: 100%; }
  .dm-hero__spec > div { grid-template-columns: 4.75rem 1fr; gap: 0.7rem; }
}

@media (prefers-reduced-motion: reduce) {
  .dm-platform__panel, .dm-platform__accent, .dm-platform__accent-badge,
  .dm-platform__swatch, .dm-platform__phone img,
  .dm-carousel__arrow, .dm-toggle__thumb,
  .dm-carousel .dm-phone__screen img { transition: none; }
  .dm-carousel__phone-wrap.is-switching .dm-phone { animation: none; }
}
