Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804/* CM6 Live Preview Editor Styles */@import '../components/external-link.css';
/* ---- Editor container ---- */.cm6-editor-container { height: 100%; width: 100%; overflow: hidden;}
.cm6-editor-container .cm-editor { height: 100%;}
.cm6-editor-container .cm-scroller { overflow: auto; scrollbar-width: thin; scrollbar-color: var(--text-tertiary) var(--bg-primary); /* Chromium stops touch-action negotiation at the nearest scroll container. * The shell's pan-y rule alone cannot keep horizontal pointerup alive here. */ touch-action: pan-y; font-family: var(--font-text, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif); font-size: var(--font-size-text, 16px); line-height: var(--line-height-text, 1.6); padding: 1rem; overscroll-behavior-y: none;}
/* Older Chromium builds use WebKit scrollbar styling instead of scrollbar-width. */.cm6-editor-container .cm-scroller::-webkit-scrollbar { width: 8px; height: 8px;}
@media (max-width: 767px) { .cm6-editor-container .cm-scroller { padding-inline: 1.5rem; }}
.cm6-editor-container .cm-content { max-width: 42rem; margin: 0 auto;}
/* Type Records are an opaque, contenteditable=false block widget. The * surrounding markdown remains the selection/navigation surface. */.cm-block-record-sections { container-type: inline-size; box-sizing: border-box; margin: 2px auto; max-width: min(100%, 38rem); padding: 2px 0.5rem 0.45rem; border: 1px solid color-mix(in srgb, var(--text-secondary) 25%, transparent); border-radius: 2px; background: var(--background-secondary);}
.cm-block-record-sections .reams-record-section + .reams-record-section { margin-top: 0.35rem;}
.cm-root-frontmatter-surface { --reams-frontmatter-cell-padding: 0.35rem; --reams-frontmatter-control-height: calc(1.6rem + 2px); --reams-frontmatter-value-inset: calc(0.35rem + 1px); display: grid; width: 100%; max-width: 100%; margin: 0 0 1rem; padding: 0; border: 0; border-radius: 0; background: transparent; gap: 0.3rem; margin-bottom: 1rem; overflow: hidden;}
.cm-root-frontmatter-surface .reams-record-fields label,.cm-root-frontmatter-surface .reams-record-fields input:not([type="checkbox"]),.cm-root-frontmatter-surface .reams-record-fields select,.cm-root-frontmatter-surface .reams-record-fields textarea,.cm-root-frontmatter-surface .reams-record-fields fieldset { min-height: 1.75rem;}
.reams-root-types,.reams-yaml-section { min-width: 0;}
.reams-root-types { display: grid; grid-template-columns: 8rem minmax(0, 1fr); align-items: start; gap: 0.2rem;}
.reams-root-section-label { padding: 0 var(--reams-frontmatter-cell-padding); color: var(--text-secondary); font-size: 0.875em; font-weight: 400;}
.reams-root-type-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem;}
.reams-root-type-chips .reams-record-type-chip { gap: 0.2rem;}
.reams-root-type-chips .reams-record-type-chip button,.reams-root-type-chips > button,.reams-yaml-field > button,.reams-yaml-sequence-item > button,.reams-yaml-sequence > button,.reams-yaml-add,.reams-yaml-add-form button,.reams-root-add,.reams-root-add-type-form button { min-width: 1.75rem; min-height: 1.75rem; padding: 0 0.25rem; border: 0; border-radius: 2px; background: transparent; color: var(--text-tertiary); cursor: pointer; font: inherit;}
.reams-root-type-chips .reams-record-type-chip button:hover,.reams-root-type-chips > button:hover,.reams-yaml-field > button:hover,.reams-yaml-sequence-item > button:hover,.reams-yaml-sequence > button:hover,.reams-yaml-add:hover,.reams-yaml-add-form button:hover,.reams-root-add:hover,.reams-root-add-type-form button:hover { background: var(--bg-hover); color: var(--text-primary);}
.reams-root-type-chips select,.reams-yaml-field input,.reams-yaml-field select,.reams-yaml-field textarea,.reams-yaml-add-form input,.reams-yaml-add-form select,.reams-root-add-type-form select { min-width: 0; min-height: 1.75rem; box-sizing: border-box; padding: 0 var(--reams-frontmatter-cell-padding); border: 1px solid transparent; border-radius: 2px; background: transparent; color: var(--text-primary); font: inherit; font-size: 0.875em;}
.reams-root-type-chips select:hover,.reams-yaml-field input:hover,.reams-yaml-field select:hover,.reams-yaml-field textarea:hover,.reams-yaml-add-form input:hover,.reams-yaml-add-form select:hover,.reams-root-add-type-form select:hover { border-color: var(--text-tertiary);}
.reams-root-type-chips select:focus-visible,.reams-root-type-chips button:focus-visible,.reams-yaml-field input:focus-visible,.reams-yaml-field select:focus-visible,.reams-yaml-field textarea:focus-visible,.reams-yaml-field button:focus-visible,.reams-yaml-add-form input:focus-visible,.reams-yaml-add-form select:focus-visible,.reams-yaml-add-form button:focus-visible,.reams-yaml-add:focus-visible,.reams-root-add:focus-visible,.reams-root-add-type-form select:focus-visible,.reams-root-add-type-form button:focus-visible { outline: none; border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); background: var(--bg-primary, transparent);}
.reams-yaml-fields,.reams-yaml-map,.reams-yaml-sequence,.reams-yaml-tags { display: grid; gap: 0.25rem; min-width: 0;}
.reams-yaml-map,.reams-yaml-sequence { width: 100%; padding-left: 0.7rem; border-left: 1px solid var(--border-color); box-sizing: border-box;}
.reams-yaml-field { display: grid; grid-template-columns: 8rem minmax(0, 1fr) auto; align-items: start; gap: 0.2rem; min-width: 0; overflow: hidden;}
.reams-yaml-key { width: 100%; color: var(--text-secondary) !important;}
.reams-yaml-value,.reams-yaml-value > input,.reams-yaml-value > textarea,.reams-yaml-value > select { min-width: 0; width: 100%;}
.reams-yaml-value textarea { min-height: 4rem; resize: vertical;}
.reams-yaml-sequence-item { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: start; gap: 0.2rem; min-width: 0;}
.reams-yaml-index { padding-top: 0.3rem; color: var(--text-tertiary); font-size: 0.75rem;}
.reams-yaml-add-form { display: grid; grid-template-columns: 8rem minmax(5rem, 0.5fr) minmax(6rem, 1fr) auto auto; gap: 0.2rem; min-width: 0;}
.reams-yaml-tags { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.25rem;}
.cm-root-frontmatter-surface .reams-yaml-tags button { min-width: 1.75rem; min-height: 1.75rem;}
.reams-root-additions { display: flex; flex-wrap: wrap; align-items: start; gap: 0.2rem 0.75rem; min-width: 0; padding-top: 0.15rem;}
.reams-root-additions > .reams-yaml-add,.reams-root-additions > .reams-root-add { width: auto; padding-inline: var(--reams-frontmatter-cell-padding); color: var(--text-tertiary);}
.reams-root-add-type-form { display: flex; align-items: center; gap: 0.2rem; min-width: 0;}
.cm-root-frontmatter-surface .reams-record-section { min-width: 0;}
.cm-root-frontmatter-surface .reams-record-fields { padding-top: 0.1rem;}
.cm-root-frontmatter-surface .reams-record-fields label { grid-template-columns: 8rem minmax(0, 1fr); gap: 0.2rem; min-height: 1.75rem; padding: 0; overflow: hidden;}
.cm-root-frontmatter-surface .reams-record-fields label > span { padding: 0 var(--reams-frontmatter-cell-padding); font-size: 0.875em;}
.cm-root-frontmatter-surface .reams-record-fields input:not([type="checkbox"]),.cm-root-frontmatter-surface .reams-record-fields select,.cm-root-frontmatter-surface .reams-record-fields textarea,.cm-root-frontmatter-surface .reams-record-fields fieldset { padding: 0 var(--reams-frontmatter-cell-padding); border-color: transparent; border-radius: 2px; background: transparent;}
.cm-root-frontmatter-surface .reams-record-fields input:not([type="checkbox"]):hover,.cm-root-frontmatter-surface .reams-record-fields select:hover,.cm-root-frontmatter-surface .reams-record-fields textarea:hover,.cm-root-frontmatter-surface .reams-record-fields fieldset:hover { border-color: var(--text-tertiary);}
.cm-root-frontmatter-surface .reams-record-fields input:not([type="checkbox"]):focus,.cm-root-frontmatter-surface .reams-record-fields select:focus,.cm-root-frontmatter-surface .reams-record-fields textarea:focus { outline: none; border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); background: var(--bg-primary, transparent);}
.cm-root-frontmatter-surface .reams-record-type-chip { min-height: 1.2rem; padding: 0 0.35rem;}
@media (max-width: 640px) { .cm-root-frontmatter-surface { width: 100%; margin-inline: 0; }
.reams-yaml-field { grid-template-columns: minmax(0, 1fr) auto; gap: 0.25rem; padding-block: 0.2rem; }
.reams-yaml-key { grid-column: 1; min-height: 2.5rem !important; padding-inline: var(--reams-frontmatter-cell-padding); }
.reams-yaml-value { grid-column: 1 / -1; grid-row: 2; }
.reams-yaml-field > .reams-yaml-remove { grid-column: 2; grid-row: 1; }
.reams-root-type-chips button, .reams-root-type-chips select, .reams-yaml-field input, .reams-yaml-field select, .reams-yaml-field textarea, .reams-yaml-field button, .reams-yaml-sequence button, .reams-yaml-add, .reams-yaml-add-form input, .reams-yaml-add-form select, .reams-yaml-add-form button, .reams-root-add, .reams-root-add-type-form select, .reams-root-add-type-form button { min-width: 2.5rem; min-height: 2.5rem; }
.reams-yaml-add-form { grid-template-columns: minmax(0, 1fr) minmax(6rem, 0.7fr); }
.reams-root-additions, .reams-root-add-type-form { width: 100%; }
.cm-root-frontmatter-surface .reams-yaml-tags button { min-width: 2.5rem; min-height: 2.5rem; }
.reams-root-types { grid-template-columns: minmax(0, 1fr); gap: 0.1rem; }
.cm-root-frontmatter-surface .reams-record-fields label { grid-template-columns: minmax(0, 1fr); gap: 0.15rem; min-height: 0; padding-block: 0.2rem; }
.cm-root-frontmatter-surface .reams-record-fields label > span { padding-inline: var(--reams-frontmatter-cell-padding); }
.cm-root-frontmatter-surface .reams-record-fields input:not([type="checkbox"]), .cm-root-frontmatter-surface .reams-record-fields select, .cm-root-frontmatter-surface .reams-record-fields textarea, .cm-root-frontmatter-surface .reams-record-fields fieldset { min-height: 2.5rem; }
.cm-root-frontmatter-surface .reams-root-type-chips .reams-record-type-chip button { min-width: 2.5rem; min-height: 2.5rem; }}
/* Root frontmatter deliberately reuses the legacy properties-table DOM and * visual vocabulary. Only Record disclosure and recursive containers are new. */.cm-root-frontmatter-surface { display: block; width: 100%; max-width: 100%; margin: 0; padding: 0; overflow: visible;}
.cm-root-frontmatter-surface .cm-frontmatter-properties-table { margin-bottom: 1em; max-width: 100%; overflow: hidden;}
.cm-root-frontmatter-surface .reams-root-types { display: flex; align-items: flex-start; gap: 0.2em; padding-bottom: 0.3em; border-bottom: 1px solid var(--border-color);}
.reams-root-types + .reams-root-records,.reams-root-record-group + .reams-root-record-group { margin-top: 0.45em;}
.reams-root-records { display: grid; gap: 0.35em;}
.reams-root-record-group + .reams-root-record-group { margin-top: 0;}
.reams-root-record-group { padding: 0.25em 0.35em 0.35em 0; background: color-mix(in srgb, var(--bg-secondary) 72%, transparent);}
.reams-yaml-structure-label { padding: 0 2px 3px; color: var(--text-tertiary); font-size: 0.72em; font-weight: 600; line-height: 1.4;}
.reams-root-record-group .cm-frontmatter-prop-value > input:not([type="checkbox"]),.reams-root-record-group .cm-frontmatter-prop-value > select,.reams-root-record-group .cm-frontmatter-prop-value > textarea,.reams-root-record-group .cm-frontmatter-prop-value > .reams-input-suggest > input { background: var(--bg-primary);}
.reams-root-record-group .reams-yaml-map,.reams-root-record-group .reams-yaml-sequence,.reams-root-record-group .reams-yaml-json-scalar { container-type: inline-size; gap: 0; margin-block: 0.2em; padding: 5px; border-left: 0; background: var(--bg-primary);}
.reams-root-record-group .reams-yaml-map,.reams-root-record-group .reams-yaml-sequence { width: calc(100% - var(--reams-frontmatter-value-inset)); margin-inline-start: var(--reams-frontmatter-value-inset);}
.cm-root-frontmatter-surface .cm-frontmatter-prop-row[data-record-field-kind="json"] > .cm-frontmatter-prop-key-readonly { padding-top: calc(0.2em + 5px);}
.reams-record-url-link { display: inline-flex; align-items: center; min-width: 0; max-width: 100%; box-sizing: border-box; padding-inline-start: var(--reams-frontmatter-value-inset); color: var(--accent); text-decoration: underline; cursor: pointer;}
.reams-record-url-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.reams-root-record-group .reams-yaml-value > .reams-record-url-link { width: 100%; height: var(--reams-frontmatter-control-height); padding-inline: var(--reams-frontmatter-cell-padding); border: 1px solid transparent; border-radius: 2px; background: var(--bg-secondary); font-size: 0.875em;}
.reams-root-record-group .reams-yaml-json-scalar { display: grid;}
.reams-root-record-group .reams-yaml-json-scalar > input { width: 100%; min-width: 0; box-sizing: border-box; padding: 0 var(--reams-frontmatter-cell-padding); border: 1px solid transparent; border-radius: 2px; color: var(--text-primary); background: var(--bg-secondary); font: inherit;}
.reams-root-record-group .reams-yaml-sequence-item > input,.reams-root-record-group .reams-yaml-sequence-item > select,.reams-root-record-group .reams-yaml-sequence-item > textarea { width: 100%; min-width: 0; box-sizing: border-box; padding: 0 var(--reams-frontmatter-cell-padding); border: 1px solid transparent; border-radius: 2px; color: var(--text-primary); background: var(--bg-secondary); font: inherit;}
.reams-root-record-group .reams-yaml-field input,.reams-root-record-group .reams-yaml-field select,.reams-root-record-group .reams-yaml-field textarea { background: var(--bg-secondary);}
.reams-root-record-group .reams-yaml-field > .reams-yaml-key,.reams-root-record-group .reams-yaml-field > .reams-yaml-value > input,.reams-root-record-group .reams-yaml-field > .reams-yaml-value > select { height: var(--reams-frontmatter-control-height); min-height: var(--reams-frontmatter-control-height);}
.reams-root-record-group .reams-yaml-field > .reams-yaml-remove { width: 1.5rem; min-width: 1.5rem; padding-inline: 0;}
.reams-root-record-group .reams-yaml-sequence-item > input:hover,.reams-root-record-group .reams-yaml-sequence-item > select:hover,.reams-root-record-group .reams-yaml-sequence-item > textarea:hover { border-color: var(--text-tertiary);}
.reams-root-record-group .reams-yaml-sequence-item > input:focus-visible,.reams-root-record-group .reams-yaml-sequence-item > select:focus-visible,.reams-root-record-group .reams-yaml-sequence-item > textarea:focus-visible { outline: none; border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent);}
@media (max-width: 640px) { .cm-root-frontmatter-surface .cm-frontmatter-prop-row[data-record-field-kind="json"] > .cm-frontmatter-prop-key-readonly { padding-top: calc(0.2em + 14px); }
.reams-root-record-group .reams-yaml-field { grid-template-columns: minmax(3.5rem, 0.7fr) minmax(0, 1.3fr) 24px; align-items: center; gap: 4px; padding-block: 5px; }
.reams-root-record-group .reams-yaml-field > .reams-yaml-key, .reams-root-record-group .reams-yaml-field > .reams-yaml-value, .reams-root-record-group .reams-yaml-field > button { grid-column: auto; grid-row: auto; }
.reams-root-record-group .reams-yaml-key, .reams-root-record-group .reams-yaml-value > input, .reams-root-record-group .reams-yaml-value > select, .reams-root-record-group .reams-yaml-sequence-item > input, .reams-root-record-group .reams-yaml-sequence-item > select, .reams-root-record-group .reams-yaml-sequence-item > textarea, .reams-root-record-group .reams-yaml-field > button, .reams-root-record-group .reams-yaml-sequence-item > button, .reams-root-record-group .reams-yaml-sequence > button { height: 34px; min-height: 34px !important; }
.reams-root-record-group .reams-yaml-field > .reams-yaml-remove { width: 24px; min-width: 24px; padding-inline: 0; }
.reams-root-record-group .reams-yaml-json-scalar > input { height: 34px; min-height: 34px; }
.reams-root-record-group .reams-yaml-value > .reams-record-url-link { height: 34px; min-height: 34px; }
.reams-root-record-group .reams-yaml-sequence-item { grid-template-columns: 14px minmax(0, 1fr) 34px; align-items: center; gap: 4px; min-height: 44px; padding-block: 5px; }
.reams-root-record-group .reams-yaml-index { padding-top: 0; text-align: center; }
.reams-root-record-group .reams-yaml-sequence > button { justify-self: end; margin-top: 5px; padding-inline: 8px; }}
@container (max-width: 180px) { .reams-root-record-group .reams-yaml-field { grid-template-columns: minmax(0, 1fr) 24px; }
.reams-root-record-group .reams-yaml-field > .reams-yaml-key { grid-column: 1; grid-row: 1; }
.reams-root-record-group .reams-yaml-field > .reams-yaml-value { grid-column: 1 / -1; grid-row: 2; }
.reams-root-record-group .reams-yaml-field > button { grid-column: 2; grid-row: 1; }}
.reams-root-types-disclosure { position: relative; min-height: 1.4em; padding: 0; border: 0; background: transparent; cursor: pointer; font: inherit;}
.reams-root-types-disclosure:hover,.reams-root-types-disclosure:focus-visible { color: var(--text-primary);}
.reams-root-types-disclosure:focus-visible { outline: none; border-radius: 2px; box-shadow: inset 0 0 0 2px var(--accent);}
@media (pointer: coarse) { .reams-root-types-disclosure { position: relative; }
.reams-root-types-disclosure::after { content: ''; position: absolute; width: 44px; height: 44px; left: 50%; top: 50%; transform: translate(-50%, -50%); }}
.reams-yaml-object-summary { padding-inline: var(--reams-frontmatter-value-inset) 0.35em; color: var(--text-tertiary); font-size: 0.9em;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-value > .cm-frontmatter-prop-list { display: flex; flex: 1 1 auto; flex-wrap: wrap; align-items: flex-start; gap: 0.25em; min-width: 0; padding-inline-start: var(--reams-frontmatter-value-inset);}
.cm-root-frontmatter-surface .cm-frontmatter-prop-value.cm-frontmatter-prop-list { padding-inline-start: var(--reams-frontmatter-value-inset);}
.cm-root-frontmatter-surface .reams-root-types > .cm-frontmatter-prop-value:where(*) { flex-direction: column; flex-wrap: nowrap; gap: 0; box-sizing: border-box;}
.cm-root-frontmatter-surface .reams-root-type-chip { align-self: flex-start;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-row[data-type="checkbox"] .cm-frontmatter-prop-checkbox { margin-inline-start: var(--reams-frontmatter-value-inset);}
.cm-root-frontmatter-surface .reams-record-derived-value { padding-inline-start: var(--reams-frontmatter-value-inset);}
.cm-root-frontmatter-surface .cm-frontmatter-prop-value > input:not([type="checkbox"]),.cm-root-frontmatter-surface .cm-frontmatter-prop-value > select,.cm-root-frontmatter-surface .cm-frontmatter-prop-value > textarea { flex: 1 1 12em; width: 100%; min-width: 0; max-width: 100%; box-sizing: border-box; padding: 0 0.35em; border: 1px solid transparent; border-radius: 2px; background: transparent; color: var(--text-primary); font: inherit;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-value > input:not([type="checkbox"]):hover,.cm-root-frontmatter-surface .cm-frontmatter-prop-value > select:hover,.cm-root-frontmatter-surface .cm-frontmatter-prop-value > textarea:hover { border-color: var(--text-tertiary);}
.cm-root-frontmatter-surface .cm-frontmatter-prop-value > input:not([type="checkbox"]):focus,.cm-root-frontmatter-surface .cm-frontmatter-prop-value > select:focus,.cm-root-frontmatter-surface .cm-frontmatter-prop-value > textarea:focus { outline: none; border-color: var(--accent); background: var(--bg-primary, transparent);}
.reams-root-additions { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5em;}
.reams-root-legacy-add-form { display: grid; grid-template-columns: 8em minmax(7rem, 0.7fr) minmax(9rem, 1.2fr) auto auto; align-items: center; flex: 1 1 30em; min-width: 0; gap: 0.25em;}
.reams-root-legacy-add-form .cm-frontmatter-prop-add-name,.reams-root-legacy-add-form .cm-frontmatter-prop-add-value { width: 100%; min-width: 0; box-sizing: border-box; flex-basis: auto;}
.reams-root-add-type-form { grid-template-columns: minmax(8rem, 1fr) auto auto; flex-basis: 18em;}
.reams-root-add-structure { min-width: 0; padding: 0 0.35em; color: var(--text-tertiary); font-size: 0.9em;}
.reams-frontmatter-icon-editor { display: inline-flex; align-items: center; min-width: 0; gap: 0.3em;}
.reams-frontmatter-date-editor { display: inline-grid; grid-template-columns: minmax(0, 1fr) 1.75em; flex: 1 1 12em; align-items: center; min-width: 0;}
.reams-frontmatter-date-editor > .cm-frontmatter-prop-input,.reams-frontmatter-date-editor > .cm-frontmatter-prop-add-value { flex: 1 1 auto; width: auto; min-width: 0;}
.reams-frontmatter-date-picker-button { position: relative; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 1.75em; width: 1.75em; height: 1.75em; border: 1px solid transparent; border-radius: 2px; color: var(--text-tertiary); font-size: 0.85em; overflow: hidden; cursor: pointer;}
.reams-frontmatter-date-picker-button:hover { color: var(--text-primary); background: var(--bg-hover);}
.reams-frontmatter-date-picker { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; opacity: 0; cursor: pointer;}
.reams-frontmatter-date-picker-button:has(.reams-frontmatter-date-picker:focus-visible) { box-shadow: inset 0 0 0 1px var(--accent);}
.reams-frontmatter-icon-editor .cm-frontmatter-prop-input,.reams-frontmatter-icon-editor .cm-frontmatter-prop-add-value { flex: 1 1 auto; min-width: 0;}
.reams-frontmatter-icon-preview { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 1.25em; width: 1.25em; height: 1.25em; color: var(--text-secondary);}
.reams-frontmatter-icon-preview svg { width: 1em; height: 1em;}
@media (max-width: 640px) { .reams-root-legacy-add-form { grid-template-columns: 4em minmax(7rem, 1fr); flex-basis: 100%; }
.reams-root-legacy-add-form .reams-root-add-value, .reams-root-legacy-add-form .reams-root-add-structure { grid-column: 1 / -1; }
.reams-root-add-type-form { grid-template-columns: minmax(0, 1fr) auto auto; }
.cm-root-frontmatter-surface .cm-frontmatter-prop-add-btn, .cm-root-frontmatter-surface .cm-frontmatter-prop-add-cancel { min-height: 2.5rem; }}
/* The root metadata surface inherits the established frontmatter table as its * visual contract. These rules cover only the new Type groups and add flows. */.cm-root-frontmatter-surface .reams-root-additions { display: flex; flex-wrap: wrap; align-items: start; gap: 0.1em 0.75em; width: 100%;}
.cm-root-frontmatter-surface .reams-root-additions > .cm-frontmatter-prop-add-btn,.cm-root-frontmatter-surface .reams-root-additions > .reams-root-legacy-add-form { flex: 0 0 auto;}
.cm-root-frontmatter-surface .reams-root-additions > .reams-root-legacy-add-form:not(.reams-root-add-type-form) { flex-basis: 100%;}
.cm-root-frontmatter-surface .reams-root-additions > .reams-root-add-type-form { flex-basis: 100%;}
.cm-root-frontmatter-surface .reams-root-legacy-add-form { display: flex; align-items: flex-start; gap: 0.2em; width: 100%; min-width: 0;}
.cm-root-frontmatter-surface .reams-root-add-kind { flex: 0 0 6.75em; width: 6.75em; min-width: 0; appearance: auto;}
.cm-root-frontmatter-surface .reams-root-legacy-add-form .reams-root-add-name { flex: 0 0 4em; width: 4em; min-width: 0;}
.cm-root-frontmatter-surface .reams-root-add-value { flex: 1 1 8em; width: auto; min-width: 0;}
.cm-root-frontmatter-surface .reams-root-add-value > .cm-frontmatter-prop-list { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.25em; min-width: 0;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-key-readonly { overflow: hidden; overflow-wrap: normal; text-overflow: ellipsis; white-space: nowrap;}
.cm-root-frontmatter-surface .reams-root-add-checkbox { display: inline-flex; align-items: center; min-height: var(--reams-frontmatter-control-height); padding-inline: 0.35em;}
.cm-root-frontmatter-surface .reams-root-legacy-add-form > div.reams-root-add-value { align-items: center; min-height: var(--reams-frontmatter-control-height); box-sizing: border-box;}
.cm-root-frontmatter-surface .reams-root-add-type-form { display: flex; align-items: center; flex: none; width: auto;}
.cm-root-frontmatter-surface .reams-root-add-type-form .cm-frontmatter-prop-add-value { flex: 0 1 12em; width: auto; appearance: auto; font-size: 1em;}
.cm-root-frontmatter-surface .reams-root-add-type-form .cm-frontmatter-prop-add-btn:hover,.cm-root-frontmatter-surface .reams-root-add-type-form .cm-frontmatter-prop-add-btn:focus { background: transparent; color: var(--accent);}
.cm-root-frontmatter-surface .reams-root-add-type-form .cm-frontmatter-prop-add-cancel:hover { background: transparent; color: var(--text-primary);}
.cm-root-frontmatter-surface .reams-frontmatter-date-editor { display: inline-grid; grid-template-columns: minmax(0, 1fr) 1.75em; flex: 1 1 8em; align-items: center; min-width: 0;}
.cm-root-frontmatter-surface .reams-frontmatter-date-error { grid-column: 1 / -1; color: var(--error); font-size: 0.75em; line-height: 1.2;}
.reams-input-suggest { position: relative; display: inline-flex; flex: 1 1 auto; min-width: 0;}
.reams-input-suggest > input { width: 100%; min-width: 0;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-value .reams-input-suggest > input { box-sizing: border-box; padding: 0 0.35em; border: 1px solid transparent; border-radius: 2px; outline: none; background: transparent; color: var(--text-primary); font: inherit;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-value .reams-input-suggest > input:hover { border-color: var(--text-tertiary);}
.cm-root-frontmatter-surface .cm-frontmatter-prop-value .reams-input-suggest > input:focus { border-color: var(--accent); background: var(--bg-primary, transparent);}
.reams-input-suggest-menu { position: fixed; box-sizing: border-box; z-index: 1200; display: grid; max-height: 14rem; padding: 0.2rem; border: 1px solid var(--border-color); border-radius: 0.25rem; background: var(--bg-secondary); box-shadow: 0 6px 18px rgb(0 0 0 / 28%); overflow-y: auto;}
.reams-input-suggest-menu > button { min-height: 1.75rem; padding: 0.25rem 0.45rem; border: 0; border-radius: 0.2rem; background: transparent; color: var(--text-primary); font: inherit; text-align: left; text-overflow: ellipsis; white-space: nowrap; overflow: hidden;}
.reams-input-suggest-menu > button:hover,.reams-input-suggest-menu > button.is-selected { background: var(--bg-hover);}
.reams-reference-list { display: flex; flex: 1 1 auto; flex-wrap: wrap; align-items: center; gap: 0.25em; min-width: 0;}
.reams-reference-editor { display: flex; min-width: 0; flex-wrap: wrap; align-items: center; gap: 0.25rem;}
.reams-reference-editor > .reams-input-suggest { flex: 1 1 8rem; min-width: 7rem;}
.reams-reference-editor.is-empty { width: 100%;}
.reams-reference-editor.is-empty > .reams-reference-add { flex: 1 1 auto; width: 100%;}
.reams-reference-chip { display: inline-flex; align-items: center; gap: 0.15em; min-height: 1.5rem; padding-left: 0.35em; border-radius: 0.25rem; background: var(--bg-secondary); color: var(--text-secondary);}
.reams-reference-chip-with-wikilink { padding-left: 0; background: transparent;}
.reams-reference-chip > button { width: 1.5rem; min-width: 1.5rem; min-height: 1.5rem; margin-inline: -0.25rem -0.35rem; padding: 0; border: 0; border-radius: inherit; background: transparent; color: var(--text-tertiary); font: inherit; line-height: 1; cursor: pointer;}
.reams-reference-add { min-height: 1.5rem; padding: 0 0.4rem; border: 1px solid color-mix(in srgb, var(--text-secondary) 35%, transparent); border-radius: 2px; background: transparent; color: var(--text-secondary); font: inherit; font-size: 0.875em; line-height: 1; cursor: pointer;}
.reams-reference-add:hover { border-color: var(--text-secondary); background: var(--bg-hover); color: var(--text-primary);}
.reams-reference-add:focus-visible,.reams-reference-chip > button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px;}
.cm-root-frontmatter-surface .reams-frontmatter-date-editor > input[aria-invalid="true"] { border-color: var(--error);}
.cm-root-frontmatter-surface .reams-frontmatter-date-editor > input[type="text"] { flex: 1 1 auto; width: auto; min-width: 0;}
.reams-frontmatter-date-picker-icon,.reams-frontmatter-date-picker-icon svg { display: block; width: 0.8em; height: 0.8em;}
@media (min-width: 768px) { .cm-root-frontmatter-surface .reams-root-legacy-add-form .reams-root-add-name { flex-basis: 8em; width: 8em; }}
@media (max-width: 520px) { .cm-root-frontmatter-surface .reams-root-legacy-add-form:not(.reams-root-add-type-form) { display: grid; grid-template-columns: 6.75em minmax(0, 1fr) auto; align-items: center; }
.cm-root-frontmatter-surface .reams-root-legacy-add-form:not(.reams-root-add-type-form) .reams-root-add-kind { grid-column: 1; grid-row: 1; }
.cm-root-frontmatter-surface .reams-root-legacy-add-form:not(.reams-root-add-type-form) .reams-root-add-name { grid-column: 2; grid-row: 1; width: 100%; }
.cm-root-frontmatter-surface .reams-root-legacy-add-form:not(.reams-root-add-type-form) .reams-root-add-value { grid-column: 1 / 3; grid-row: 2; width: 100%; }
.cm-root-frontmatter-surface .reams-root-legacy-add-form:not(.reams-root-add-type-form) > .cm-frontmatter-prop-add-btn { grid-column: 3; grid-row: 2; }
.cm-root-frontmatter-surface .reams-root-legacy-add-form:not(.reams-root-add-type-form) > .cm-frontmatter-prop-add-cancel { grid-column: 3; grid-row: 1; }}
.reams-record-type-chip { min-height: 1.2rem; font-weight: 650; letter-spacing: 0.02em;}
.reams-record-date-editor { position: relative; display: inline-flex; align-items: center; min-width: 0;}
.reams-record-date-editor > input { width: 100%; min-width: 0;}
.reams-record-fields { display: grid; gap: 0.05rem; padding-top: 0.25rem;}
.reams-record-fields label { display: grid; grid-template-columns: minmax(4.5rem, 0.55fr) minmax(7rem, 1.45fr); align-items: center; gap: 0.4rem; min-width: 0; min-height: 1.7rem; padding: 0.1rem 0;}
.reams-record-fields label > span { color: var(--text-secondary); font-size: 0.875em;}
.reams-record-fields input:not([type="checkbox"]),.reams-record-fields select,.reams-record-fields textarea { min-width: 0; width: 100%; box-sizing: border-box; min-height: 1.7rem; padding: 0.15rem 0.4rem; border: 1px solid color-mix(in srgb, var(--text-secondary) 45%, transparent); border-radius: 2px; background: var(--background-primary); color: var(--text-primary); font: inherit; font-size: 0.875em;}
.reams-record-fields textarea { min-height: 3.4rem; resize: vertical;}
.reams-record-fields input:not([type="checkbox"]):hover,.reams-record-fields select:hover,.reams-record-fields textarea:hover { border-color: var(--text-secondary);}
.reams-record-fields input:not([type="checkbox"]):focus,.reams-record-fields select:focus,.reams-record-fields textarea:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent);}
.reams-record-fields input[type="checkbox"] { width: 1rem; height: 1rem; margin: 0; accent-color: var(--accent); cursor: pointer;}
.reams-record-fields fieldset { min-width: 0; margin: 0; padding: 0.25rem 0.4rem; border: 1px solid color-mix(in srgb, var(--text-secondary) 35%, transparent); border-radius: 2px;}
.reams-record-derived-value { min-width: 0; overflow: hidden; color: var(--text-secondary); font-size: 0.875em; text-overflow: ellipsis; white-space: nowrap;}
.cm-record-node-glyph { display: inline-grid; place-items: center; width: 1.35rem; height: 1.5rem; padding: 0; border: 0; border-radius: 2px; background: transparent; color: var(--text-tertiary); cursor: pointer; font: inherit; line-height: 1;}
.cm-record-node-glyph svg,.cm-list-bullet-glyph-record svg { width: 1em; height: 1em; display: block; }
.cm-record-node-glyph:hover,.cm-list-bullet-glyph-record:hover { color: var(--accent); background: var(--bg-hover); }.cm-record-node-glyph[data-active="true"],.cm-list-bullet-glyph-record[data-active="true"] { color: var(--accent); }.cm-record-node-glyph[aria-expanded="false"]:not([data-active="true"]),.cm-list-bullet-glyph-record[aria-expanded="false"]:not([data-active="true"]) { opacity: 0.58; }.cm-record-node-glyph:focus-visible,.cm-list-bullet-glyph-record:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (pointer: coarse) { .cm-record-node-glyph { width: 2.25rem; height: 2.25rem; margin-inline: -0.35rem; } .reams-record-fields label { min-height: 2.25rem; } .reams-record-fields input:not([type="checkbox"]), .reams-record-fields select { height: 1.7rem; min-height: 1.7rem; }}
@media (max-width: 640px) { .cm-block-record-sections { width: calc(100% + 1rem); max-width: none; margin: 2px -0.5rem; padding: 2px 0.75rem 0.3rem; border-radius: 2px; }
.reams-record-type-chip { min-height: 1.1rem; padding-inline: 0.25rem; border-radius: 2px; }
.reams-record-fields { gap: 0; padding-top: 0.1rem; }
.reams-record-fields label { grid-template-columns: minmax(4.25rem, 0.5fr) minmax(0, 1.5fr); gap: 0.25rem; padding-block: 0; }}
/* CM6 makes `.cm-scroller` a flex container. The content root is therefore a * flex item, and its default flex-item `min-width: auto` lets wide frontmatter * widgets pin the item's min-content width to 42rem on phones. That expands the * editor scroller horizontally and steals swipe gestures from the app shell. * Force the content root to shrink inside the scroller so frontmatter can * reflow vertically without widening the editor. */.cm6-editor-container .cm-scroller > .cm-content { min-width: 0 !important;}
/* ---- Heading styles (decoration layer — live-preview line classes) ---- */.cm-heading { font-weight: 700; }.cm-heading-1 { font-size: 2em; line-height: 1.2; }.cm-heading-2 { font-size: 1.5em; line-height: 1.25; }.cm-heading-3 { font-size: 1.17em; line-height: 1.3; }.cm-heading-4 { font-size: 1em; line-height: 1.4; }.cm-heading-5 { font-size: 0.83em; line-height: 1.45; }.cm-heading-6 { font-size: 0.67em; line-height: 1.5; }
/* ---- Hidden formatting markers ---- */.cm-formatting-hidden { /* HYBRID IDIOM 2 — see hidden-text-classes.ts (#269). * * Used by heading-decoration.ts to hide the `# ` / `## ` / etc. * prefix on non-cursor lines. Pre-fix `display: none` removed the * prefix from layout entirely; for an empty heading whose only * visible content is `# `, the line had no caret-targetable surface * and ArrowUp/ArrowDown skipped it. * * Post-fix HYBRID idiom: zero VISIBLE width (so heading text * still starts at column 0) but NON-ZERO height (so an empty * heading line has a caret-targetable inline-block). * * `display: inline-block; width: 0; max-width: 0; overflow: hidden` * zeroes visible inline width — heading text after the prefix * starts at column 0, matching pre-fix layout. * * `height: 1em; line-height: 1em` * keeps non-zero vertical extent, so an empty `# ` line has * caret-targetable geometry. This is what makes it different * from the rolled-back idiom that re-introduces the cursor- * skip bug. * * `color: transparent; user-select: none` * defensive — paint nothing if `overflow: hidden` doesn't * fully clip in some browser quirk; prevent drag-selection. * * The heading line decoration (`.cm-heading-N` font-size + * line-height) provides the dominant visual treatment * regardless of prefix visibility. */ display: inline-block; width: 0; max-width: 0; height: 1em; line-height: 1em; overflow: hidden; color: transparent; user-select: none;}
/* Blockquote/callout prefixes need a less aggressive collapse than * `display: none`. Using zero-width inline-block keeps the DOM positions * participating in caret motion, which avoids ArrowUp/ArrowDown jumping out * of long quoted blocks in some browsers. * * `height: 0; line-height: 0; vertical-align: top` mirrors the same fix * applied to `.cm-inline-formatting-hidden` below: without these, the * inline-block inherits the parent line-height and grows the surrounding * line box on top of the natural inline content's ascent/descent. * Visible callout chrome (background, padding) usually masks the resulting * line jump, but the underlying CSS pathology is identical to the * inline-formatting-hidden case — apply prophylactically so a future * theme change doesn't surface it. */.cm-blockquote-formatting-hidden { display: inline-block; width: 0; max-width: 0; height: 0; line-height: 0; vertical-align: top; overflow: hidden; white-space: pre; opacity: 0;}
.cm-inline-formatting-hidden { /* CLICK-SNAP FIX (#269 add-on): `display: none` removes the marker * span from layout AND from caret hit-testing entirely — clicks at * the visible left edge of `**bold**` resolve to position 'b' * (start of the visible content), not to the source position of the * hidden `**` marker. The earlier (pre-fix) zero-geometry inline- * block kept the marker in the DOM as a zero-rect hit target; * caretRangeFromPoint snapped to it, producing the user-reported * "cursor lands at marker boundary" bug. * * Why `display: none` is safe here even though #269 used the * zero-geometry idiom for the same-shape problem on whole-line * collapses: this class is only ever applied to INLINE markers * (bold/italic/strike/`** `/`*`/`~~`/backticks). The line ALWAYS * has visible text on at least one side of the marker (the markup * always wraps content). The whole-line cursor-skip mechanism that * #269 originally addressed required a `.cm-line` whose ENTIRE * visible content was zero-collapsed — that does not occur for * inline markers, so `display: none` here cannot regress the * cursor-skip class. * * Belt-and-braces: the inline-format-decoration plugin and * code-marker-decoration plugin also register their hidden-marker * RangeSets via `EditorView.atomicRanges` (see those files' * doc-comments and node_modules/@codemirror/view/dist/index.d.ts: * 1311-1315 — the documented way to make collapsing decorations * behave atomically for cursor motion). atomicRanges' boundary * caveat (`pos > from && pos < to`, see index.js:3708) means * positions exactly at `from`/`to` are still passed through; the * `display: none` rendering ensures caretRangeFromPoint never * resolves to those boundary positions in the first place. * * Affects: bold/italic/strike marker collapse (inline-format-decoration.ts) * and inline/fenced code marker collapse (code-marker-decoration.ts). */ display: none;}
/* ---- Inline formatting styles (decoration layer) ---- */.cm-bold { font-weight: 700; }.cm-italic { font-style: italic; }.cm-strikethrough { text-decoration: line-through; }
/* Wikilink chip styling lives in src/components/wikilink-chip.css so editor * widgets and sidebar link panels share one canonical visual. */
/* ---- Markdown syntax classes: LAYOUT (all modes) * * These rules apply in every mode. They cover weight/style/font-family — * the bits that are part of the structural visual, not coloring. Colors * live under `[data-mode="source"]` below so live-preview mode stays * monochrome prose with only weight/italic/etc cues. * * TWO-LAYER ARCHITECTURE (Strategy Gate decision #8 in * docs/plans/2026-04-22-syntax-highlighting-visual-theming.md): * - HighlightStyle classes (cm-md-*) are emitted by the language parser * via syntaxHighlighting(). They cover source mode and are the only * token classes present when live-preview is disabled. * - Decoration ViewPlugin classes (cm-heading, cm-bold, cm-italic, * cm-strikethrough, plus .cm-heading-1..6 line classes) are applied * by extensions in src/editor/extensions/*. They cover live-preview * mode and also drive the per-heading font-size stack. * The two layers deliberately overlap on font-weight / font-style so * that either layer alone produces the correct visual. Font-size lives * only in the decoration layer to avoid compounding. * * SCOPE NOTE for `.cm-md-list` and `.cm-md-quote`: * @lezer/markdown applies `tags.list` / `tags.quote` to the ENTIRE * list-item / quote-line content (marker + body), not only to the * marker. Applying a color rule to `.cm-md-list` would tint every list * item's body text. We therefore leave `.cm-md-list` with no color. * Blockquote `.cm-md-quote` keeps `font-style: italic` (a prose * convention expected for the whole line). * -------------------------------------------------------------------------- */.cm-md-strong { font-weight: 700; }.cm-md-em { font-style: italic; }.cm-md-strike { text-decoration: line-through; }.cm-md-quote { font-style: italic; }.cm-md-code { font-family: var(--font-mono); }
/* Inline `code` chrome — sunken background, small padding, rounded. * * Scoped via :not(.cm-codeblock *) so the inline-code visual chrome only * applies to backtick spans in prose, NOT to the cm-md-code spans that * appear inside a fenced/indented code block (those already get the * `.cm-codeblock` slab via the line decoration). The line-class selector * `.cm-line.cm-codeblock .cm-md-code` would suppress this rule with the * higher-specificity `:not()` form below. * * Visible in all modes: source, live-preview, and read. The inline-code * convention is universal and the chrome is muted enough to not dominate. */.cm-line:not(.cm-codeblock) .cm-md-code { background-color: var(--code-bg-sunken); padding: 0.05em 0.35em; border-radius: 0.25em; /* Hide the surrounding backticks visually only in non-source modes; * source mode still wants them for editing. The .cm-md-punctuation * tertiary tint already provides enough contrast in source. */}.cm-md-comment { font-style: italic; }
/* ---- Markdown syntax classes: COLORS (source mode only) * * Per user decision: colors apply only when the editor is showing raw * markdown (source mode). Live-preview mode renders prose-like text with * font weight/style only. Read mode hides syntax markers entirely (see * `[data-mode="read"]` block farther down). * * HEADING SPECIFICS: * The warm-orange heading tint applies only in source mode. Font-weight * is intentionally NOT set on `.cm-md-heading` — the user wants color * alone to distinguish heading text in source mode. In live-preview * mode the `.cm-heading` line decoration still applies `font-weight: 700` * via the decoration layer. * * LIST MARKER EXCEPTION: * Every list item's marker (`-`, `*`, `1.`) is tagged by @lezer/markdown * as BOTH `list` and `processingInstruction`, so the marker span carries * `cm-md-list cm-md-punctuation`. We exempt list markers from the * punctuation dim tint by overriding to `inherit` when both classes are * present — keeps markers readable in the default text color. * -------------------------------------------------------------------------- */.cm6-editor-container[data-mode="source"] .cm-md-heading { color: var(--syntax-heading); }.cm6-editor-container[data-mode="source"] .cm-md-code { color: var(--syntax-code); }.cm6-editor-container[data-mode="source"] .cm-md-link { color: var(--accent); }.cm6-editor-container[data-mode="source"] .cm-md-url { color: var(--accent); }.cm6-editor-container[data-mode="source"] .cm-md-hr { color: var(--text-tertiary); }.cm6-editor-container[data-mode="source"] .cm-md-meta { color: var(--syntax-callout); }.cm6-editor-container[data-mode="source"] .cm-md-punctuation { color: var(--text-tertiary); }.cm6-editor-container[data-mode="source"] .cm-md-comment { color: var(--text-tertiary); }.cm6-editor-container[data-mode="source"] .cm-md-wikilink-bracket { color: var(--text-tertiary); }.cm6-editor-container[data-mode="source"] .cm-md-label { color: var(--text-secondary); }.cm6-editor-container[data-mode="source"] .cm-md-escape { color: var(--text-tertiary); }.cm6-editor-container[data-mode="source"] .cm-md-invalid { color: var(--error); }.cm6-editor-container[data-mode="source"] .cm-md-frontmatter { color: var(--syntax-frontmatter); }.cm6-editor-container[data-mode="source"] .cm-md-frontmatter-fence { color: var(--text-tertiary); }
/* ---- Markdown link rendering (live-preview + read mode) ---- * * `cm-md-inline-link-label` is the visible label of a `[label](url)` * link when the cursor is far from it (a Decoration.replace widget * collapsing brackets/URL/parens). `cm-md-bare-url` covers * `<https://…>` autolink URL ranges and top-level GFM bare URL ranges. * * The accent + underline + pointer cursor combine to communicate "this * is a hyperlink" — matching browser default link affordance. *//* Link labels and external arrows share components/external-link.css with UI links. */
/* Source mode: bare URLs keep their existing accent colour (set on * line 250 by the `.cm-md-url` rule) and gain an underline as a * web-browser-style affordance. NO new colour is added; this rule * intentionally adds ONLY text-decoration. */.cm6-editor-container[data-mode="source"] .cm-md-url { text-decoration: underline;}
/* External-link icon (diagonal arrow): rendered as a separate * `Decoration.widget` (`ExternalLinkIconWidget` in * inline-link-decoration.ts) and emitted at the end of each link / * bare-URL range, mirroring `CalloutIconWidget`'s emission pattern. * * Conditional emission — the widget is NOT added when the link is on * the cursor's line. This is the same chrome-hiding-on-cursor-line * idiom callout/blockquote use. We deliberately do NOT use a CSS * `display: none` toggle here: that would fail the * `cm6-editor-css-hide-audit` test, which exists specifically to * prevent display-none-as-suppression patterns on `.cm-line` * descendants. * * Inline `<svg>` with `stroke="currentColor"` so the icon inherits * the surrounding link text colour automatically — no per-theme * overrides needed. Source mode never emits the widget at all (the * inline-link-decoration extension is mounted only via livePreview()). * Wikilink chips (`.cm-wikilink-chip`) are NOT decorated with this * widget; they are internal navigation, not external. *//* Frontmatter override: the line class paints the whole line with * --syntax-frontmatter, but nested parser spans (cm-md-heading, cm-md-link, * cm-md-punctuation, etc.) would otherwise override that inherited color. * Clamp every nested cm-md-* descendant back to inherit, EXCEPT the fence * marks which intentionally stay tertiary gray. */.cm6-editor-container[data-mode="source"] .cm-md-frontmatter [class*="cm-md-"]:not(.cm-md-frontmatter-fence) { color: inherit !important; font-weight: inherit !important; font-style: inherit !important; text-decoration: none !important; font-family: inherit !important;}
/* List marker exemption: an element with BOTH cm-md-list and cm-md-punctuation * is a list marker (`-`, `*`, `1.`). Keep it in the default text color in * source mode rather than dimmed. Specificity: 0,3,0 (three classes) beats * the `.cm6-editor-container[data-mode="source"] .cm-md-punctuation` rule * above which is 0,2,0 + one attribute = 0,3,0. Tie-break by source order * puts this rule last, so it wins. */.cm6-editor-container[data-mode="source"] .cm-md-list.cm-md-punctuation { color: inherit; }
/* Also exempt link-marker brackets that appear inside wikilinks from the * punctuation tint — the outer-bracket decoration will already paint them * via cm-md-wikilink-bracket. Without this override, the inner `[` and `]` * inside `[[target]]` would remain tertiary-gray while the OUTER brackets * are wikilink-bracket tertiary-gray too — visually identical, so this is * cosmetic. Left in place for documentation. */
/* ---- Callout blocks (live-preview + read mode) ---- * * Callout styling uses CSS-only chrome: border-left for type color, * background tint, and ::before for icons (future). * * NO ch-based offset math. Line decorations apply to .cm-line elements * and CSS handles all layout. * * Source mode note: calloutDecoration is only loaded via livePreview(), * which is disabled in source mode. These styles only apply in * live-preview and read modes. * -------------------------------------------------------------------------- *//* Each type-specific rule declares the rgba fallback first, then attempts * the `color-mix(in srgb, ...)` derivation. Browsers that don't support * `color-mix()` (Safari <16.2, Firefox <113) drop the second rule and fall * back to the rgba precomputed in `--syntax-callout-<type>-bg`. Browsers * that DO support it use the derivation, which keeps the tint in lockstep * with the solid color variable if a future palette tweak only changes the * solid hex. *//* Blockquote / callout chrome with ancestor flow-through. * * Layout: * - `margin-left: (depth-1) * indent`. Each nesting level visually sits * INSIDE its parent: the inner block's box starts to the right of * where the parent's chrome ends. This is the classic "nested boxes" * reading. * - `padding-left: 1em` for the content gutter inside the line. * - `border-left: bar-width solid <self-color>` for the inner block's * own accent. * - The PARENT's chrome (bar + tint at each ancestor depth) is painted * by a `::before` pseudo-element that extends LEFTWARD into the * margin gutter. The pseudo's `background-image` is a stack of * `linear-gradient` layers, one per ancestor depth, each painting a * solid stripe followed by a translucent tint at that depth's slot. * * Why ::before + linear-gradient (not background gradients on the line * itself, and not box-shadow): * - Each nested level should have its OWN clean tint with no mixing. * `background-color` on the inner block paints just the inner box, * no overlap. * - The parent's chrome lives OUTSIDE the inner block, in the margin * area painted by `::before`. The line's own background-color * doesn't reach there, so no blending across boundaries. * - Each ancestor's slot in the gradient stack is non-overlapping * (slot K spans `(K-1)*indent` to `K*indent`), so the layered * gradients render independent strips with no alpha mixing. * - Box-shadows can't paint a tall thin vertical bar OUTSIDE the * element's box without bleeding the spread vertically too; * ::before with absolute positioning is the cleanest primitive. * * The decoration extension (callout-decoration.ts:buildAncestorChainStyle) * emits per-line CSS variables (`--cm-bq-color-1`..`--cm-bq-color-N-1`) * that the static gradient stack reads. */.cm-callout, .cm-quote { /* Indent step per nesting level. The line is indented by * `(depth - 1) * --cm-bq-indent` via margin-left, so visually each * nesting level sits inside its parent. */ --cm-bq-indent: 1.5rem; --cm-bq-bar-width: 4px; --cm-bq-tint-alpha: 10%; /* Lists reserve a 6px marker rail before their glyph. Starting quote and * callout chrome on that same rail makes mixed list/callout notes scan as a * single aligned column instead of making callouts look inset or floating. */ margin-left: calc(6px + (var(--cm-blockquote-depth, 1) - 1) * var(--cm-bq-indent)); padding-left: 1em; border-left: var(--cm-bq-bar-width) solid var(--cm-bq-self-color, var(--text-tertiary)); /* Establish a positioning context so ::before can extend leftward into * the margin gutter to paint ancestor chrome. */ position: relative;}
/* Parent-chrome flow-through: a single ::before pseudo-element on every * blockquote / callout line that EXTENDS LEFTWARD from the line's left * edge into the margin gutter. The pseudo's background is a stack of * `linear-gradient`s — one per ancestor depth — each painting a thin * solid stripe (the parent's bar) followed by a translucent tint to the * right of that bar. * * Width of ::before is the total leftward distance: `(depth - 1) * indent`. * No content paints over this area because the line's own box starts at * `left: 0` of the pseudo's right edge. * * Each ancestor depth K (1..depth-1) paints at horizontal position * `(K - 1) * indent` measured from the EDITOR's left edge, i.e. from * the leftmost edge of the ::before. So the gradient stop math is * `(K-1) * indent` to `(K-1) * indent + bar-width`. * * The line's OWN box (.cm-line) is to the right of the ::before and * paints its own border + bg-color separately, so the inner block has * a clean tint with no color mixing from ancestors. * * Depth ceiling: 12 layered gradients — comfortable headroom for any * realistic Obsidian-flavored markdown. Adversary-review M1 flagged * that the JS emits `--cm-bq-color-K` for any K (no cap), so a stack * shorter than the deepest realistic doc would silently lose the * deepest ancestor's bar. 12 is more than any plausible note nesting; * if a future doc somehow exceeds it, the slot just paints transparent * (graceful degradation). */.cm-callout::before, .cm-quote::before { content: ""; position: absolute; top: 0; bottom: 0; /* Anchor the right edge of ::before at the OUTER edge of the line's * own border-left (where the margin gutter ENDS). With `position: * relative` on the line, an absolutely-positioned ::before's * containing block is the line's PADDING box — so `right: 100%` * alone would place the right edge at the inner edge of border-left, * shifted INWARD by `--cm-bq-bar-width`. Add that bar-width back so * the parent's bar painted by the gradient lines up with the actual * parent line's border-left position above and below the nested block. * * Without this offset the parent's bar in the margin would sit * ~4px to the right of the parent's bar on its own lines — a * "step" at the boundary of nested blocks. */ right: calc(100% + var(--cm-bq-bar-width)); width: calc((var(--cm-blockquote-depth, 1) - 1) * var(--cm-bq-indent)); pointer-events: none; /* Stacked gradient layers — depths 1..12. Each layer is independent * (no alpha blending) because they don't overlap horizontally; each * ancestor's stripe + tint occupies its own `indent`-wide slot. */ background-image: /* depth 1 */ linear-gradient(to right, transparent 0, transparent calc(0 * var(--cm-bq-indent)), var(--cm-bq-color-1, transparent) calc(0 * var(--cm-bq-indent)), var(--cm-bq-color-1, transparent) calc(0 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-1, transparent) var(--cm-bq-tint-alpha), transparent) calc(0 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-1, transparent) var(--cm-bq-tint-alpha), transparent) calc(1 * var(--cm-bq-indent)), transparent calc(1 * var(--cm-bq-indent)) ), /* depth 2 */ linear-gradient(to right, transparent 0, transparent calc(1 * var(--cm-bq-indent)), var(--cm-bq-color-2, transparent) calc(1 * var(--cm-bq-indent)), var(--cm-bq-color-2, transparent) calc(1 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-2, transparent) var(--cm-bq-tint-alpha), transparent) calc(1 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-2, transparent) var(--cm-bq-tint-alpha), transparent) calc(2 * var(--cm-bq-indent)), transparent calc(2 * var(--cm-bq-indent)) ), /* depth 3 */ linear-gradient(to right, transparent 0, transparent calc(2 * var(--cm-bq-indent)), var(--cm-bq-color-3, transparent) calc(2 * var(--cm-bq-indent)), var(--cm-bq-color-3, transparent) calc(2 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-3, transparent) var(--cm-bq-tint-alpha), transparent) calc(2 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-3, transparent) var(--cm-bq-tint-alpha), transparent) calc(3 * var(--cm-bq-indent)), transparent calc(3 * var(--cm-bq-indent)) ), /* depth 4 */ linear-gradient(to right, transparent 0, transparent calc(3 * var(--cm-bq-indent)), var(--cm-bq-color-4, transparent) calc(3 * var(--cm-bq-indent)), var(--cm-bq-color-4, transparent) calc(3 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-4, transparent) var(--cm-bq-tint-alpha), transparent) calc(3 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-4, transparent) var(--cm-bq-tint-alpha), transparent) calc(4 * var(--cm-bq-indent)), transparent calc(4 * var(--cm-bq-indent)) ), /* depth 5 */ linear-gradient(to right, transparent 0, transparent calc(4 * var(--cm-bq-indent)), var(--cm-bq-color-5, transparent) calc(4 * var(--cm-bq-indent)), var(--cm-bq-color-5, transparent) calc(4 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-5, transparent) var(--cm-bq-tint-alpha), transparent) calc(4 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-5, transparent) var(--cm-bq-tint-alpha), transparent) calc(5 * var(--cm-bq-indent)), transparent calc(5 * var(--cm-bq-indent)) ), /* depth 6 */ linear-gradient(to right, transparent 0, transparent calc(5 * var(--cm-bq-indent)), var(--cm-bq-color-6, transparent) calc(5 * var(--cm-bq-indent)), var(--cm-bq-color-6, transparent) calc(5 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-6, transparent) var(--cm-bq-tint-alpha), transparent) calc(5 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-6, transparent) var(--cm-bq-tint-alpha), transparent) calc(6 * var(--cm-bq-indent)), transparent calc(6 * var(--cm-bq-indent)) ), /* depth 7 */ linear-gradient(to right, transparent 0, transparent calc(6 * var(--cm-bq-indent)), var(--cm-bq-color-7, transparent) calc(6 * var(--cm-bq-indent)), var(--cm-bq-color-7, transparent) calc(6 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-7, transparent) var(--cm-bq-tint-alpha), transparent) calc(6 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-7, transparent) var(--cm-bq-tint-alpha), transparent) calc(7 * var(--cm-bq-indent)), transparent calc(7 * var(--cm-bq-indent)) ), /* depth 8 */ linear-gradient(to right, transparent 0, transparent calc(7 * var(--cm-bq-indent)), var(--cm-bq-color-8, transparent) calc(7 * var(--cm-bq-indent)), var(--cm-bq-color-8, transparent) calc(7 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-8, transparent) var(--cm-bq-tint-alpha), transparent) calc(7 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-8, transparent) var(--cm-bq-tint-alpha), transparent) calc(8 * var(--cm-bq-indent)), transparent calc(8 * var(--cm-bq-indent)) ), /* depth 9 */ linear-gradient(to right, transparent 0, transparent calc(8 * var(--cm-bq-indent)), var(--cm-bq-color-9, transparent) calc(8 * var(--cm-bq-indent)), var(--cm-bq-color-9, transparent) calc(8 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-9, transparent) var(--cm-bq-tint-alpha), transparent) calc(8 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-9, transparent) var(--cm-bq-tint-alpha), transparent) calc(9 * var(--cm-bq-indent)), transparent calc(9 * var(--cm-bq-indent)) ), /* depth 10 */ linear-gradient(to right, transparent 0, transparent calc(9 * var(--cm-bq-indent)), var(--cm-bq-color-10, transparent) calc(9 * var(--cm-bq-indent)), var(--cm-bq-color-10, transparent) calc(9 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-10, transparent) var(--cm-bq-tint-alpha), transparent) calc(9 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-10, transparent) var(--cm-bq-tint-alpha), transparent) calc(10 * var(--cm-bq-indent)), transparent calc(10 * var(--cm-bq-indent)) ), /* depth 11 */ linear-gradient(to right, transparent 0, transparent calc(10 * var(--cm-bq-indent)), var(--cm-bq-color-11, transparent) calc(10 * var(--cm-bq-indent)), var(--cm-bq-color-11, transparent) calc(10 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-11, transparent) var(--cm-bq-tint-alpha), transparent) calc(10 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-11, transparent) var(--cm-bq-tint-alpha), transparent) calc(11 * var(--cm-bq-indent)), transparent calc(11 * var(--cm-bq-indent)) ), /* depth 12 */ linear-gradient(to right, transparent 0, transparent calc(11 * var(--cm-bq-indent)), var(--cm-bq-color-12, transparent) calc(11 * var(--cm-bq-indent)), var(--cm-bq-color-12, transparent) calc(11 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-12, transparent) var(--cm-bq-tint-alpha), transparent) calc(11 * var(--cm-bq-indent) + var(--cm-bq-bar-width)), color-mix(in srgb, var(--cm-bq-color-12, transparent) var(--cm-bq-tint-alpha), transparent) calc(12 * var(--cm-bq-indent)), transparent calc(12 * var(--cm-bq-indent)) );}
.cm-quote { --cm-bq-bar-width: 2px; --cm-bq-self-color: var(--text-tertiary);}.cm-callout { --cm-bq-self-color: var(--syntax-callout-note); background-color: var(--syntax-callout-note-bg); background-color: color-mix(in srgb, var(--syntax-callout-note) 10%, transparent);}
.cm-callout-header { font-weight: 600;}
/* Type-specific accent color and background tint. * * `--cm-bq-self-color` is consumed by the static `border-left: * var(--cm-bq-bar-width) solid var(--cm-bq-self-color)` rule on * `.cm-callout, .cm-quote` to paint the current depth's vertical bar. * The translucent `background-color` here paints the line's own tint * — the inner block's box. Ancestor bars and tints are painted by the * `::before` pseudo-element using `--cm-bq-color-K` vars emitted from * the decoration extension's `buildAncestorChainStyle`. */.cm-callout-note { --cm-bq-self-color: var(--syntax-callout-note); background-color: var(--syntax-callout-note-bg); background-color: color-mix(in srgb, var(--syntax-callout-note) 10%, transparent);}.cm-callout-abstract { --cm-bq-self-color: var(--syntax-callout-abstract); background-color: var(--syntax-callout-abstract-bg); background-color: color-mix(in srgb, var(--syntax-callout-abstract) 10%, transparent);}.cm-callout-info { --cm-bq-self-color: var(--syntax-callout-info); background-color: var(--syntax-callout-info-bg); background-color: color-mix(in srgb, var(--syntax-callout-info) 10%, transparent);}.cm-callout-todo { --cm-bq-self-color: var(--syntax-callout-todo); background-color: var(--syntax-callout-todo-bg); background-color: color-mix(in srgb, var(--syntax-callout-todo) 10%, transparent);}.cm-callout-tip { --cm-bq-self-color: var(--syntax-callout-tip); background-color: var(--syntax-callout-tip-bg); background-color: color-mix(in srgb, var(--syntax-callout-tip) 10%, transparent);}.cm-callout-success { --cm-bq-self-color: var(--syntax-callout-success); background-color: var(--syntax-callout-success-bg); background-color: color-mix(in srgb, var(--syntax-callout-success) 10%, transparent);}.cm-callout-question { --cm-bq-self-color: var(--syntax-callout-question); background-color: var(--syntax-callout-question-bg); background-color: color-mix(in srgb, var(--syntax-callout-question) 10%, transparent);}.cm-callout-warning { --cm-bq-self-color: var(--syntax-callout-warning); background-color: var(--syntax-callout-warning-bg); background-color: color-mix(in srgb, var(--syntax-callout-warning) 10%, transparent);}.cm-callout-failure { --cm-bq-self-color: var(--syntax-callout-failure); background-color: var(--syntax-callout-failure-bg); background-color: color-mix(in srgb, var(--syntax-callout-failure) 10%, transparent);}.cm-callout-danger { --cm-bq-self-color: var(--syntax-callout-danger); background-color: var(--syntax-callout-danger-bg); background-color: color-mix(in srgb, var(--syntax-callout-danger) 10%, transparent);}.cm-callout-bug { --cm-bq-self-color: var(--syntax-callout-bug); background-color: var(--syntax-callout-bug-bg); background-color: color-mix(in srgb, var(--syntax-callout-bug) 10%, transparent);}.cm-callout-example { --cm-bq-self-color: var(--syntax-callout-example); background-color: var(--syntax-callout-example-bg); background-color: color-mix(in srgb, var(--syntax-callout-example) 10%, transparent);}.cm-callout-quote { --cm-bq-self-color: var(--syntax-callout-quote); background-color: var(--syntax-callout-quote-bg); background-color: color-mix(in srgb, var(--syntax-callout-quote) 10%, transparent);}
/* ---- Callout type icons (#252) ---- * * `cm-callout-icon-wrapper` is the <span> emitted by CalloutIconWidget; * it wraps the Lucide-built <svg class="cm-callout-icon cm-callout-icon-<type>">. * * Layout intent: the icon sits BEFORE the rendered title (or before the * hidden `> [!type]` chrome on inactive lines), inline-flex with a small * margin-inline-end (NOT flex `gap` — see the wrapper rule below) so it * has consistent breathing room from the title text in both live-preview * and read mode. * * `vertical-align: -0.125em` aligns the 1em-square SVG with the text * baseline (Lucide canvas centers the glyph 0.5em above baseline; dropping * by 0.125em puts the optical center close to the cap height of body text). * * The icon stroke uses currentColor (set on the SVG itself), and the * per-type rules below set `color` on `.cm-callout-<type> .cm-callout-icon` * so the stroke matches each type's accent. We do this on the icon, NOT * on the surrounding line, because the line text needs to inherit normal * prose color — only the icon should pick up the type accent. * -------------------------------------------------------------------------- */.cm-callout-icon-wrapper { display: inline-flex; align-items: center; /* Breathing room between the icon and the callout title text. The * wrapper contains only the icon SVG; the title is the next inline * sibling, so the gap is a margin-inline-end (not flex `gap` — `gap` * has nothing to act on with a single child). */ margin-inline-end: 0.5em;}
.cm-callout-icon { vertical-align: -0.125em; flex-shrink: 0;}
.cm-callout-note .cm-callout-icon { color: var(--syntax-callout-note); }.cm-callout-abstract .cm-callout-icon { color: var(--syntax-callout-abstract); }.cm-callout-info .cm-callout-icon { color: var(--syntax-callout-info); }.cm-callout-todo .cm-callout-icon { color: var(--syntax-callout-todo); }.cm-callout-tip .cm-callout-icon { color: var(--syntax-callout-tip); }.cm-callout-success .cm-callout-icon { color: var(--syntax-callout-success); }.cm-callout-question .cm-callout-icon { color: var(--syntax-callout-question); }.cm-callout-warning .cm-callout-icon { color: var(--syntax-callout-warning); }.cm-callout-failure .cm-callout-icon { color: var(--syntax-callout-failure); }.cm-callout-danger .cm-callout-icon { color: var(--syntax-callout-danger); }.cm-callout-bug .cm-callout-icon { color: var(--syntax-callout-bug); }.cm-callout-example .cm-callout-icon { color: var(--syntax-callout-example); }.cm-callout-quote .cm-callout-icon { color: var(--syntax-callout-quote); }
/* ---- Plain blockquote (no [!type] header) ---- * * Applied by calloutDecoration when a `> text` line is NOT inside any * callout block. Distinguished from callouts by: * - Thinner left border (2px vs 4px) * - Muted border color (text-tertiary, not a callout palette color) * - No background tint * - Italic body text — matches the typographic convention for quoted prose * * The leading `> ` on inactive lines is already hidden via * `.cm-blockquote-formatting-hidden` (same mechanism callouts use), so the body text aligns visually with the * padding-left offset. * * Mode scope: calloutDecoration is only loaded via `livePreview()` (see * `src/editor/extensions/live-preview.ts`), which is disabled in source mode. * These styles therefore only affect live-preview and read modes. If that * wiring ever changes, the source-mode override rules in the preceding * `[data-mode="source"]` block would need to disable `.cm-quote` colors too. * -------------------------------------------------------------------------- */.cm-quote { /* Layout (margin-left, padding-left, border-left) and the inline * accent bar all come from the shared `.cm-callout, .cm-quote` rule * above, with `--cm-bq-self-color` and `--cm-bq-bar-width` set to * plain-quote values (text-tertiary, 2px) earlier. Only the * typographic affordances live here. */ font-style: italic; color: var(--text-secondary);}
/* ---- Horizontal rule (live-preview + read mode, #218) ---- * * `cm-md-hr-line` is the line class emitted by hrDecoration on every * Lezer `HorizontalRule` node (live-preview-only by virtue of the * extension being registered via livePreview()). The visible separator * is a `border-top` painted on a `::before` pseudo-element so the rule * sits in the VERTICAL CENTER of the padded line, rather than on the * top edge (where it would look attached to the previous block). * * `cm-md-hr-line-hidden` is the mark decoration over the raw `---` / * `***` / `___` text emitted on inactive lines. It uses the SAME * collapse pattern as `.cm-inline-formatting-hidden`: zero width, zero * line-height, opacity 0 — keeping a DOM presence so caret motion and * CM6's line measurement continue working while the visible glyphs * disappear and the border alone reads as the rule. * * Source mode visual treatment: NONE. The decoration extension is not * registered in source mode, and we do NOT add a source-mode-scoped * rule for `.cm-md-hr-line` here. Source mode shows raw text only. * -------------------------------------------------------------------------- */.cm-md-hr-line { /* Line keeps its normal height so caret motion isn't surprised by a * 0-height line. The visible rule is painted on the ::before * pseudo-element below (centered between the top and bottom padding), * NOT on the line's own border edge. The hidden raw text is collapsed * by .cm-md-hr-line-hidden. * * `padding-block` (not `margin-block`) — line classes apply to * `.cm-line` elements that CM6 lays out and measures itself. Vertical * margins can shift line metrics in ways that confuse cursor * positioning. The existing line-class convention in this file uses * padding-block for breathing room (see `.cm-heading-*` and * `.cm-codeblock`). Lock that idiom for HR too. */ padding-block: 0.75em; position: relative;}
/* Visible rule, centered in the padded line, ONLY when the raw * dashes are hidden. When the cursor is on the HR line the hide * mark is omitted and the dashes show; we suppress the painted * rule there so the user sees clean source (just `---`) instead of * a horizontal line striking through the dashes. * * `:has(.cm-md-hr-line-hidden)` is supported in all evergreen * browsers (Chrome 105+, Safari 15.4+, Firefox 121+). The PWA * targets these baselines. */.cm-md-hr-line:has(.cm-md-hr-line-hidden)::before { /* - block-level so it lays out as its own row inside the .cm-line, * - top: 50% + translateY(-50%) places its top edge at the line's * vertical center, * - the border-top then sits at that center point. * This keeps the HR visually equidistant from neighboring paragraphs * regardless of the padding-block value. */ content: ""; display: block; position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); border-top: 1px solid var(--text-tertiary);}
.cm-md-hr-line-hidden { /* WHOLE-LINE HIDE — see hidden-text-classes.ts idiom 2 (#269). * * The line's only visible content is the raw `---`. Zero-geometry * collapse (the previous `width:0; height:0; line-height:0`) leaves * CM6 with no caret-targetable surface — ArrowDown skips the line. * * `color: transparent` paints nothing while preserving full text * geometry; `user-select: none` keeps an invisible click+drag from * highlighting the dashes. The visible separator is painted on the * gated `.cm-md-hr-line:has(.cm-md-hr-line-hidden)::before` rule, * which already exists and is unchanged by this fix. */ color: transparent; user-select: none;}
/* ---- Fenced & indented code blocks (decoration layer) ---- * * `cm-codeblock` is emitted as a line class by code-fence-decoration.ts * on every line inside a FencedCode or CodeBlock Lezer node. Inline * `code` spans (which carry `cm-md-code`) are intentionally NOT styled * here — they keep their tertiary-text monospace treatment from the * source-mode rules above. * * Visible in source AND live-preview modes (the decoration extension is * always-on, not gated behind livePreviewCompartment). Read mode keeps * the same chrome for now — we can opt out later by scoping to a * `:not([data-mode="read"])` ancestor if it proves distracting. * * Token coloring (`.tok-*`) is scoped to `.cm-codeblock` so language * tokens don't bleed into inline code, prose, or markdown source spans. * The classHighlighter installed in CM6Editor.tsx assigns the classes; * this CSS controls their appearance inside fenced blocks only. * --------------------------------------------------------------------- */.cm-codeblock { background-color: var(--code-bg-sunken); font-family: var(--font-mono); padding-inline: 0.75em; /* Vertical breathing room: per-line padding (decoration-layer effect). * * Round-1 review #14 noted that `padding-block` applied per-`.cm-line` * contributes line-internal vertical chrome, NOT outer-block daylight, * because the next line's background tile sits flush against the * previous one. We keep this intentionally: * * 1) The actual ask was vertical breathing room. With `0.15em` per * line of top+bottom padding, each fenced line is 0.3em taller * than a prose line. Adjacent prose paragraphs already provide * ample daylight via paragraph margins. * 2) Adding outer block padding via `:first-of-type / :last-of-type` * would require knowing where blocks start/end without coupling to * the decoration's tree-walk — `.cm-line.cm-codeblock:first-of-type` * selects only the first .cm-line in the editor, not per-block. * Solving this cleanly would mean emitting a different decoration * class on the open/close fence lines, doubling the decoration * surface for marginal visual gain. * * If outer-block padding becomes a real ask later, the right next step * is to emit `cm-codeblock-first` / `cm-codeblock-last` line classes * from code-fence-decoration.ts and target those here. */ padding-block: 0.15em;}
/* Token colors scoped to fenced blocks. * * Maps every `tok-*` class @lezer/highlight's `classHighlighter` emits to * a `--code-syntax-*` theme variable declared in `src/index.css`. Each * `--code-syntax-*` var defaults to a value in the prose / callout * palette so the two move together by default, but a custom theme can * override any single one to decouple. * * Near-synonyms group onto one rule (e.g., `tok-string2` joins `tok-string`) * so the rule list stays compact rather than a 30:9 repetition. * * `tok-labelName` does NOT map onto the variable color here. The language * info string on a fence (` ```typescript `) is the only span carrying * `tok-labelName` we want to style — and we want to DIM it, not paint it * the variable color. To avoid hitting in-language label tokens (e.g. C * goto labels) and accidentally dimming real code, the dim rule scopes to * spans that ALSO carry `cm-md-label` (the markdown layer's label tag, * which is uniquely the fence info string). See dedicated rule below. */.cm-codeblock .tok-keyword { color: var(--code-syntax-keyword); }.cm-codeblock .tok-atom,.cm-codeblock .tok-bool,.cm-codeblock .tok-literal { color: var(--code-syntax-atom); }.cm-codeblock .tok-string,.cm-codeblock .tok-string2,.cm-codeblock .tok-url,.cm-codeblock .tok-link { color: var(--code-syntax-string); }.cm-codeblock .tok-number { color: var(--code-syntax-number); }.cm-codeblock .tok-comment { color: var(--code-syntax-comment); font-style: italic; }.cm-codeblock .tok-variableName,.cm-codeblock .tok-variableName2,.cm-codeblock .tok-namespace { color: var(--code-syntax-variable); }.cm-codeblock .tok-propertyName,.cm-codeblock .tok-macroName { color: var(--code-syntax-property); }/* `definition` variant of variableName/propertyName: function definitions * (`function foo(...)`) and object-literal keys (`{ foo: 1 }`). Use the * property color so callable names and member keys read consistently. */.cm-codeblock .tok-variableName.tok-definition,.cm-codeblock .tok-propertyName.tok-definition { color: var(--code-syntax-property); }.cm-codeblock .tok-typeName,.cm-codeblock .tok-className { color: var(--code-syntax-type); }.cm-codeblock .tok-operator,.cm-codeblock .tok-punctuation { color: var(--code-syntax-operator); }.cm-codeblock .tok-meta { color: var(--code-syntax-meta); }.cm-codeblock .tok-invalid { color: var(--error); }/* Markdown-as-fenced-block constructs that the lang-markdown nested grammar * may emit when a doc has nested ` ```markdown ` fences: keep these * synchronized with the prose layer so they don't visually clash. */.cm-codeblock .tok-heading { color: var(--code-syntax-keyword); font-weight: 600; }.cm-codeblock .tok-strong { font-weight: 700; }.cm-codeblock .tok-emphasis { font-style: italic; }
/* Language label dim treatment. * * Adversary review #1: a naive `.cm-codeblock .tok-labelName` rule would * also dim real in-language label tokens (e.g. C goto labels), since * `tags.labelName` from @lezer/highlight is not unique to fence info. * Tighten the selector to only spans carrying BOTH `tok-labelName` AND * `cm-md-label` — the latter is emitted by our markdown HighlightStyle * exclusively for fence info strings, so it's a reliable discriminant. * * Adversary review #3: the existing `.cm6-editor-container[data-mode= * "source"] .cm-md-label` rule has specificity 0,2,1 with the attribute * selector. To override it in source mode we need to match or exceed — * scoping under the same `.cm6-editor-container` ancestor with the * combined `.tok-labelName.cm-md-label` brings the new rule to 0,3,0, * which beats 0,2,1. Effective in all modes (source, live-preview, read). * * Adversary review #5: dropped the 0.85em font-size shrink. Fence info * sits inline with the open ` ``` ` punctuation; mixing 1em and 0.85em * caused inconsistent line-box rhythm across monospace fallbacks. Color * + italic alone is enough to recede the label. */.cm6-editor-container .cm-codeblock .tok-labelName.cm-md-label { color: var(--code-syntax-fence-label); font-style: italic;}
.cm-editor .cm-cursor, .cm-editor .cm-dropCursor { border-left-color: var(--text-primary); border-left-width: 2px;}
.cm-editor .cm-content { caret-color: var(--text-primary);}
/* ---- Selection ---- */.cm-editor .cm-selectionBackground { background: rgba(74, 158, 255, 0.2) !important;}
/* Hide CM6's built-in selection overlay. Reams paints selection rects * through `domSelectionLayer` from the live DOM selection so widget and * wrapped-line geometry matches the browser's own range rects; leaving * the stock `.cm-selectionLayer` visible would double-paint a second, * geometry-divergent overlay on top. This is overlay chrome, not * content geometry. */.cm-editor .cm-selectionBackground,.cm-editor .cm-selectionLayer .cm-selectionBackground { /* overlay chrome */ background: rgba(74, 158, 255, 0.2) !important;}
.cm-editor .cm-selectionLayer .cm-selectionBackground { display: none;}
.cm-editor .cm-dom-selectionLayer .cm-domSelectionBackground { background: rgba(74, 158, 255, 0.2) !important;}
.cm-editor.cm-focused .cm-selectionBackground { background: rgba(74, 158, 255, 0.3) !important;}
.cm-editor.cm-focused .cm-selectionBackground,.cm-editor.cm-focused .cm-dom-selectionLayer .cm-domSelectionBackground { background: rgba(74, 158, 255, 0.3) !important;}
/* ---- Placeholder ---- */.cm-editor .cm-placeholder { color: var(--text-tertiary); font-style: italic;}
/* ---- Source mode: monospace font ---- */.cm6-editor-container[data-mode="source"] .cm-scroller { font-family: var(--font-mono);}
/* ---- Read mode visuals (subtle + hide comments) * * Applied whenever the container has data-mode="read". CSS-only; no CM6 * Compartment, Facet, or extension is needed for this. The decoration * extensions still run, but the read-mode container CSS hides the punctuation * and comment spans so the text reads like prose. * * Visual changes: * - Subtle bg tint (bg-secondary) to distinguish from edit mode * - Cursor and drop cursor fully hidden; caret-color:transparent prevents * the native caret from appearing if something tries to focus the content * - cm-md-punctuation hidden (headings mark, list mark, etc.) * - cm-md-comment hidden (entire <!-- ... --> block, including delimiters) * - cm-md-wikilink-bracket hidden (raw [[]] brackets when a chip isn't shown) * - cm-md-frontmatter-fence hidden (--- lines bordering frontmatter) * * CURSOR SPECIFICITY NOTE: CM6's default theme declares * `.cm-editor.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor { display: block }` * (specificity 0,4,1). Our simpler selector * `.cm6-editor-container[data-mode="read"] .cm-cursor` is 0,3,0 — losing * the cascade. We use `!important` plus a descendant match including * `.cm-cursorLayer` to guarantee override. `!important` is the idiomatic * escape hatch for overriding library defaults; acceptable here because * read mode is an explicit override of normal editor behavior. * -------------------------------------------------------------------------- */
.cm6-editor-container[data-mode="read"] { background-color: var(--bg-secondary);}
/* CURSOR-SKIP JUSTIFICATION (#269): read mode has no caret — the * cursor layers are forcibly hidden so the editor never advertises an * insertion point in read-only mode. `display: none` here is safe * even though it would otherwise be flagged by the * cursor-skip-decoration audit — read mode has no caret, so the user * cannot observe a "skipped line". */.cm6-editor-container[data-mode="read"] .cm-cursorLayer,.cm6-editor-container[data-mode="read"] .cm-cursor,.cm6-editor-container[data-mode="read"] .cm-dropCursor { display: none !important;}
.cm6-editor-container[data-mode="read"] .cm-content { caret-color: transparent;}
/* Firefox still shows the text/I-beam cursor on editable surfaces even when * the caret itself is hidden. Force the read-mode text surfaces back to the * default pointer so the editor no longer advertises an insertion point. */.cm6-editor-container[data-mode="read"] .cm-editor,.cm6-editor-container[data-mode="read"] .cm-scroller,.cm6-editor-container[data-mode="read"] .cm-content,.cm6-editor-container[data-mode="read"] .cm-line { cursor: default !important;}
/* CURSOR-SKIP JUSTIFICATION (#269): read mode has no caret (cursor * layers are display:none above and .cm-content carries * caret-color: transparent), so cursor-skip cannot manifest here even * though `display: none` would otherwise be flagged by the * cursor-skip-decoration audit. */.cm6-editor-container[data-mode="read"] .cm-md-punctuation,.cm6-editor-container[data-mode="read"] .cm-md-comment,.cm6-editor-container[data-mode="read"] .cm-md-wikilink-bracket,.cm6-editor-container[data-mode="read"] .cm-md-frontmatter-fence { display: none;}
/* Frontmatter body lines (between fences) are hidden in read mode — * frontmatter is metadata, not content to render as prose. * CURSOR-SKIP JUSTIFICATION (#269): read mode has no caret (see the * cursor-layer rule above), so `display: none` is safe here. */.cm6-editor-container[data-mode="read"] .cm-md-frontmatter { display: none;}
@media (max-width: 767px) { .cm-list-fold-control { opacity: 1; }}
/* ---- Image embed widget ---- * * Two display modes, distinguished by the wrapper's `data-mode` attribute * (set by the widget's toDOM()): * * data-mode="inline" (cursor outside embed): * Wrapper is a <span> emitted by Decoration.replace covering [from, to). * Must flow with surrounding paragraph text. Image is inline-block so * it can sit alongside text on the same visual line. * * data-mode="block" (cursor touches embed markup): * Wrapper is a <div> emitted by Decoration.widget({block:true,side:1}) * at line.to. CM6 places it on its own visual line below the embed. * The image fills the row. * * GEOMETRY NOTE: * Standalone embeds retain their full-size preview. Mixed-text images * fit inside a font-sized box so the surrounding line stays at text height. */.cm-image-embed[data-mode="inline"] { display: inline;}
.cm-image-embed[data-mode="block"] { display: block;}
.cm-image-embed-loading-text { color: var(--text-tertiary); font-style: italic;}
/* Error state: dashed-border chip. Specificity is bumped to beat the * data-mode selectors above (otherwise inline mode's `display: inline` * would override and the padding/border would render incorrectly). */.cm-image-embed[data-mode="inline"].cm-image-embed-error,.cm-image-embed[data-mode="block"].cm-image-embed-error { display: inline-block; padding: 0.1em 0.5em; border: 1px dashed var(--error); color: var(--error); border-radius: 0.25em; font-size: 0.9em;}
/* Image element: inline-block by default so it flows alongside text in * inline mode. In block mode we promote it to block so it fills the row. * * Standalone images (sole content of a line) render at full size. * Mixed-content images (alongside text in a paragraph) are capped at * one em in either dimension so they don't disrupt the line flow. The cap * is consistent across inline and block modes so there's no size jump * when the cursor enters/exits the embed. * (Gallery lines — 2+ images only — are handled by image-gallery-widget.) */.cm-image-embed-img { display: inline-block; vertical-align: middle; max-width: 100%;}
/* Thumbnail cap for images mixed with paragraph text */.cm-image-embed:not([data-standalone]) > .cm-image-embed-img { max-height: 1em; max-width: 1em; width: auto;}
.cm-image-embed[data-mode="block"] > .cm-image-embed-img { display: block;}
.cm-fenced-code-block-processor { --dataview-surface: color-mix(in srgb, var(--bg-primary, transparent) 92%, var(--text-primary, currentColor) 8%); --dataview-row-rule: color-mix(in srgb, var(--text-primary, currentColor) 14%, transparent); --dataview-header-rule: color-mix(in srgb, var(--text-primary, currentColor) 28%, transparent); --dataview-link: var(--accent, currentColor); --dataview-focus: var(--accent, currentColor); --dataview-cell-padding: 0.42rem 0.62rem; display: block; width: min(100%, 48rem); max-width: 100%; box-sizing: border-box; min-width: 0; overflow-x: auto; overflow-y: visible; /* CM6 measures block widgets by their element box. Keep the visual breathing room inside that box so hit-testing below the widget stays aligned with the following source line. */ margin: 0; border-top: 0.55rem solid transparent; border-bottom: 0.75rem solid transparent; padding: 0.12rem 0.2rem 0.2rem; background: var(--dataview-surface); background-clip: padding-box; border-radius: 2px; line-height: 1.45; -webkit-overflow-scrolling: touch;}
.cm-fenced-code-block-processor > div { min-width: 0;}
/* Bases use the same table surface as .base files; other processors retain the subdued code-result backdrop above. */.cm-fenced-code-block-processor[data-language="base"] { width: 100%; padding: 0; overflow-x: visible; background: transparent;}
.cm-fenced-code-block-processor table { width: max-content; min-width: 100%; border-collapse: collapse; text-align: start;}
.cm-fenced-code-block-processor th,.cm-fenced-code-block-processor td { padding: var(--dataview-cell-padding); vertical-align: top; text-align: start;}
.cm-fenced-code-block-processor :is(table.dataview, table.datacore-table) > thead > tr > th { border-bottom: 1px solid var(--dataview-header-rule); font-weight: 650; white-space: nowrap;}
/* Dataview appends its result count to the first table heading. Reams keeps * the header as the table's quiet label row; the count adds a second visual * line without helping scanability. The count is generated, non-editable * processor output, and the reading-view branch has no live caret, so hiding * it cannot affect editor caret geometry. */.cm-fenced-code-block-processor table.dataview > thead .dataview.small-text,.markdown-reading-view table.dataview > thead .dataview.small-text { display: none;}
.cm-fenced-code-block-processor :is(table.dataview, table.datacore-table) > tbody > tr > td { border-bottom: 1px solid var(--dataview-row-rule);}
.cm-fenced-code-block-processor :is(table.dataview, table.datacore-table) > tbody > tr:last-child > td { border-bottom: 1px solid var(--dataview-row-rule);}
.cm-fenced-code-block-processor :is(table.dataview, table.datacore-table) > tbody > tr:hover > td { background: color-mix(in srgb, var(--dataview-link) 6%, transparent);}
/* Datacore retains its interactive components; suppress the upstream * whole-row highlight so the shared cell treatment wins in both themes. */.cm-fenced-code-block-processor table.datacore-table > tbody > tr:hover { background-color: transparent !important;}
.cm-fenced-code-block-processor .datacore-table-group-header h2 { margin-block: 0.35rem 0.22rem; font-size: inherit; font-weight: 650;}
.cm-fenced-code-block-processor .dc-paging-control { margin-block: 0.45rem 0.25rem; gap: 0.4rem;}
.cm-fenced-code-block-processor .dc-paging-control button { min-width: 2rem; min-height: 2rem;}
.cm-fenced-code-block-processor .dc-paging-control button:focus-visible { outline: 2px solid var(--dataview-focus); outline-offset: 2px;}
.cm-fenced-code-block-processor .dataview,.cm-fenced-code-block-processor .dataview.result-group,.cm-fenced-code-block-processor ul { max-width: 100%; overflow-wrap: anywhere;}
.cm-fenced-code-block-processor .dataview.list-view-ul,.markdown-reading-view .dataview.list-view-ul { margin-block: 0.28rem 0.48rem; padding-inline-start: 1.4rem; line-height: 1.45;}
.cm-fenced-code-block-processor .dataview.list-view-ul > li,.markdown-reading-view .dataview.list-view-ul > li { margin-block: 0 !important; padding-block: 0.08rem !important; line-height: inherit;}
.cm-fenced-code-block-processor .dataview.list-view-ul > li > span > .markdown-rendered:has(> a:only-child),.markdown-reading-view .dataview.list-view-ul > li > span > .markdown-rendered:has(> a:only-child) { display: inline;}
.cm-fenced-code-block-processor .dataview.list-view-ul ul,.cm-fenced-code-block-processor .dataview.list-view-ul ol,.markdown-reading-view .dataview.list-view-ul ul,.markdown-reading-view .dataview.list-view-ul ol { margin-block: 0.1rem 0.2rem; padding-inline-start: 1.35rem; line-height: inherit;}
.cm-fenced-code-block-processor ul { margin-block: 0.28rem 0.48rem; padding-inline-start: 1.4rem;}
.cm-fenced-code-block-processor ol { margin-block: 0.28rem 0.48rem; padding-inline-start: 1.55rem;}
.cm-fenced-code-block-processor .dataview.result-group { margin-block: 0.35rem 0.65rem;}
.cm-fenced-code-block-processor .result-group { margin-block: 0.35rem 0.65rem;}
.markdown-reading-view .dataview .result-group { margin-block: 0.35rem 0.65rem;}
.cm-fenced-code-block-processor .result-group-title { margin-block: 0 0.22rem; font-weight: 650;}
.cm-fenced-code-block-processor a { color: var(--dataview-link); cursor: pointer; text-decoration-line: underline; text-decoration-thickness: 0.08em; text-underline-offset: 0.16em;}
.cm-fenced-code-block-processor a:hover { text-decoration-thickness: 0.12em;}
.cm-fenced-code-block-processor input[type="checkbox"] { width: 1rem; height: 1rem; margin: 0 0.38rem 0 0; accent-color: var(--dataview-link); vertical-align: -0.15em;}
.cm-fenced-code-block-processor input[type="checkbox"]:focus-visible,.cm-fenced-code-block-processor a:focus-visible { outline: 2px solid var(--dataview-focus); outline-offset: 2px;}
.cm-fenced-code-block-processor:focus-within { outline: 1px solid color-mix(in srgb, var(--dataview-focus) 45%, transparent); outline-offset: 2px;}
.cm-fenced-code-block-processor-loading { color: var(--text-tertiary); font-style: italic;}
.cm-fenced-code-block-processor-empty { color: var(--text-tertiary); font-size: 0.9em;}
.cm-fenced-code-block-processor-error { padding: 0.35em 0.6em; border: 1px dashed var(--error); color: var(--error); font-size: 0.9em;}
@media (max-width: 600px) { .cm-fenced-code-block-processor { width: 100%; margin-block: 0.42rem 0.6rem; padding-inline: 0.08rem; }
.cm-fenced-code-block-processor th, .cm-fenced-code-block-processor td { padding: 0.34rem 0.48rem; }}
/* ---- Task checkbox widget (live-preview + read mode) ---- * * Rendered by checkbox-widget.ts inside `livePreview()`. The extension * emits a Decoration.replace covering the 3-char TaskMarker ([ ] or [x]) * with an <input type="checkbox"> widget. * * Styling goals: * - Vertically aligned with surrounding text baseline * - Pointer cursor for discoverability * - Adequate touch target on mobile (at least 18×18 CSS px) * - No outline/border surprises in dark theme * * Read-mode cursor override: the read-mode rules above set `cursor: default` * on .cm-line. We override back to `pointer` specifically for checkboxes so * they remain discoverable as interactive elements. * -------------------------------------------------------------------------- */.cm-task-checkbox { cursor: pointer; width: 1em; height: 1em; margin: 0 0.25em 0 0; vertical-align: middle; position: relative; top: -0.1em; accent-color: var(--accent);}
.cm-task-checkbox-hitbox { position: relative;}
/* Grow only the pointer target on touch layouts. The pseudo-element has no * paint, no layout footprint, and delegates its click through the wrapper. * The wrapper deliberately has no dimensions or margins: the input retains * the exact inline geometry it had before the hit-target enhancement. */@media (hover: none) { .cm-task-checkbox-hitbox::before { content: ""; position: absolute; inset: -4px; }}
/* In read mode, restore pointer cursor on checkboxes despite the global * `cursor: default !important` on .cm-line. The !important and extra * specificity are needed to override the read-mode rule. */.cm6-editor-container[data-mode="read"] .cm-task-checkbox { cursor: pointer !important;}
/* Read mode pointer overrides: read mode globally forces * `cursor: default !important;` on `.cm-line` and friends, but link * affordances must remain pointer-cursored to match the browser default * and the user's tap intent. Mirrors the `.cm-task-checkbox` override * pattern directly above. The external-link icon is included because * it's a clickable affordance (`.cm-md-external-link-icon`'s * domEventHandlers branch resolves icon clicks to the adjacent URL); * without this override, mobile read-mode users would see no cursor * affordance over the icon despite it being clickable. */.cm6-editor-container[data-mode="read"] .cm-md-inline-link-label,.cm6-editor-container[data-mode="read"] .cm-md-bare-url,.cm6-editor-container[data-mode="read"] .cm-md-external-link-icon,.cm6-editor-container[data-mode="read"] .cm-wikilink-chip { cursor: pointer !important;}
/* ---- List bullet markers (live-preview + read mode) ---- * Rendered by list-bullet-widget.ts. Marks the 1-char ListMark so the * source marker stays in the DOM with its original width. A non-editable overlay * paints a measurable depth-specific glyph over the marker anchor, keeping * editable source geometry separate from visual marker geometry. * * Depth cycle (3-cycle): disc (•) → triangle (▸) → square (▪). * Filled glyphs all the way down: hollow shapes (◦) read as "empty" or * "incomplete" in checklist contexts and visually compete with task * checkbox affordances on `- [ ] item` lines. Filled disc / triangle / * square give clean nesting differentiation without that semantic clash. * -------------------------------------------------------------------------- */.cm-line.cm-list-item { /* The source indentation belongs to markdown syntax, not the rendered * hierarchy. Move the first visual row back by that source prefix plus the * marker gutter, then leave every wrapped row at the text column. This is a * real hanging indent rather than a cosmetic offset, so long content stays * inside its own depth on narrow screens as well as desktop. */ --cm-list-gutter: 2ch; padding-left: calc(6px + var(--cm-list-offset) + var(--cm-list-gutter)); text-indent: calc(0px - var(--cm-list-offset) - var(--cm-list-gutter)); position: relative;}
.cm-line.cm-list-task-item { /* A task marker becomes a 1em checkbox plus its inline spacing, so it needs * a wider hanging gutter than a plain bullet. */ --cm-list-gutter: 3.5ch;}
/* Completion is semantic feedback, not a source-mode transformation. The * checkbox decoration only exists in live-preview/read mode, so source text * remains completely raw. The completion pulse itself is driven by WAAPI in * checkbox-widget.ts and is intentionally live-preview-only. */.cm-task-completed { color: var(--text-tertiary); text-decoration-line: line-through; text-decoration-thickness: 1px; text-decoration-color: currentColor; /* A two-stop, double-width background lets WAAPI reveal the completed * color from left to right in lockstep with the pseudo-element strike. */ background-image: linear-gradient(to right, var(--text-tertiary) 50%, var(--text-primary) 50%); background-size: 200% 100%; background-position: 0 0; background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent;}
.cm-list-source-indent { display: inline-block; width: var(--cm-list-offset); max-width: var(--cm-list-offset); color: transparent; white-space: pre; user-select: none;}
.cm-list-bullet { display: inline-flex; justify-content: center; position: relative; width: 1ch; color: transparent;}
/* The active marker retains its editable source glyph on the cursor item; * ancestor markers keep their rendered glyph, but share the accent so the * path reads as one connected editing context. */.cm-list-bullet-source { color: var(--text-tertiary);}
.cm-list-bullet-source.cm-list-bullet-source-active { color: var(--accent);}
.cm-list-bullet-glyph.cm-list-bullet-glyph-active { color: var(--accent); transition: color 180ms cubic-bezier(0.16, 1, 0.3, 1), transform 180ms cubic-bezier(0.16, 1, 0.3, 1);}
.cm-list-bullet-glyph-record.cm-list-bullet-glyph-active { opacity: 1; }
/* Measured in list-bullet-widget.ts from the active unordered/task ancestry. * It sits behind editable content and uses an L-shaped route so each parent * rail remains vertical until it joins the deeper bullet. */.cm-list-thread { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; /* The editor content establishes its own paint layer. Keep the thread in * the glyph layer so its animated bridge is painted above that content, * while DOM order still leaves bullet glyphs on top of the line. */ z-index: 1;}
.cm-list-thread-path { fill: none; stroke: var(--accent); stroke-linecap: round; stroke-linejoin: round; stroke-opacity: 0.72; stroke-width: 1.5px;}
.cm-list-thread-bridge { stroke-opacity: 0.52; stroke-width: 2px;}
@media (prefers-reduced-motion: reduce) { .cm-list-bullet-glyph-active, .cm-list-thread-path { transition: none; }}
/* Browser-measurable glyphs live outside `.cm-content`, preserving native * selection geometry while the source marker continues to own editable width. */.cm-list-bullet-glyph-layer { position: absolute; left: 0; top: 0; overflow: visible; pointer-events: none; z-index: 1;}
/* Local-only list folding. The source stays mounted for CM6's position and * selection model; descendant rows animate their own visual height away. */.cm-line.cm-list-folded-child { max-height: 0; min-height: 0; padding-block: 0; opacity: 0; overflow: hidden;}
.cm-list-fold-control-layer { position: absolute; overflow: visible; pointer-events: none; z-index: 2;}
.cm-content { --cm-list-gutter: 2ch;}
.cm-list-fold-control { width: 2rem; height: 2rem; margin: 0; padding: 0; border: 0; border-radius: 0.25rem; appearance: none; background: transparent; color: var(--text-tertiary); cursor: pointer; font: inherit; font-size: 1.25em; line-height: 1; display: grid; place-items: center; pointer-events: auto; touch-action: manipulation; will-change: transform; transition: transform 180ms cubic-bezier(0.16, 1, 0.3, 1), color 140ms ease-out, background-color 140ms ease-out;}
.cm-list-fold-control { position: absolute; /* `left`/`top` are the measured glyph center. The translate below centers * this box on that point; an additional negative margin double-shifts it * upward and left, especially obvious on top-level items. */ transform: translate(-50%, -50%) rotate(90deg);}
.cm-list-fold-control[data-collapsed="true"] { transform: translate(-50%, -50%) rotate(0deg); }@media (prefers-reduced-motion: reduce) { .cm-list-fold-control { transition: none; }}
.cm-list-bullet-glyph { display: block; position: absolute; width: max-content; color: var(--text-tertiary); line-height: 1; pointer-events: none; white-space: nowrap; transform: translate(-50%, -50%); text-indent: 0;}
.cm-list-bullet-glyph-record { display: grid; place-items: center; width: 1.5rem; height: 1.5rem; padding: 0; border: 0; background: transparent; color: color-mix(in srgb, var(--text-tertiary) 82%, var(--bg-primary)); cursor: pointer; font: inherit; font-size: 1em; pointer-events: auto; touch-action: manipulation;}
/* Keep the editable source marker quiet; the disclosure glyph itself carries * active-line state without making the Markdown delimiter compete with it. */.cm-list-bullet-source.cm-list-bullet-record,.cm-list-bullet-source.cm-list-bullet-record.cm-list-bullet-source-active { color: color-mix(in srgb, var(--text-tertiary) 82%, var(--bg-primary));}
.cm-list-bullet-glyph-record[data-active="true"] { color: var(--accent); }
.cm-list-bullet-glyph-record:hover { color: var(--accent); }
@media (pointer: coarse) { .cm-list-bullet-glyph-record { width: 2.25rem; height: 2.25rem; }}
.cm-list-bullet-glyph-disc { font-size: 1.2em;}
/* Triangle uses U+25B8 BLACK RIGHT-POINTING SMALL TRIANGLE — points into * the nested content, reinforcing the "this contains a sub-list" reading. * Sized smaller than disc since the triangle's bounding box is taller. */.cm-list-bullet-glyph-triangle { font-size: 0.95em;}
.cm-list-bullet-glyph-square { font-size: 0.8em;}
/* Mobile list markers need a little more optical weight against touch-first * chrome; desktop proportions remain unchanged. */@media (max-width: 767px) { .cm-list-bullet-glyph-disc { font-size: 1.32em; } .cm-list-bullet-glyph-triangle { font-size: 1.05em; } .cm-list-bullet-glyph-square { font-size: 0.9em; }}
/* ---- GFM tables (live-preview + read mode) ---------------------------- *//* Rendered by table-grid.ts inside livePreview(). Each row line is a flex * container; each cm-table-cell wrapper is `flex: 1 1 0` so columns share * the row width equally. Pipes are hidden via cm-table-pipe-hidden * (zero-width flex item with `color: transparent`; see #269); the * separator row's `|---|---|` source text is wrapped by a * `Decoration.mark` carrying `cm-table-separator-anchor` (NOT a * `Decoration.replace`) — keeping the delimiter in the DOM as * caret-targetable inline content is what makes the line reachable * by ArrowUp/ArrowDown. CSS makes the glyphs visually invisible. * --cm-table-cols is still emitted on each row line as a parity / * future-styling hook (e.g. media queries that retarget per column count) * but flex distribution does not consume it. * * NOTE (Spike R4a / Fallback C): we use display:flex, NOT display:grid. * Spike R4a showed `display: grid` on a `.cm-line` makes * `view.coordsAtPos(offsetInside)` return null in jsdom — breaking mouse * click positioning and vertical motion. Flex preserves CM6's coordsAtPos * line-box assumptions. Visual outcome (uniformly distributed columns * with per-cell alignment) is identical for v1 layout requirements. */
/* CRITICAL specificity note: the `.cm-line.cm-table-row` compound * selector is REQUIRED. CM6's themed default `.ͼN .cm-line { display: * block }` (where ͼN is an autogenerated theme class) has specificity * 0,2,0. A bare `.cm-table-row` rule has 0,1,0 and silently loses, * leaving `display: block` on the row — which makes cells stack * vertically (each cell takes 100% width on its own visual line). The * compound `.cm-line.cm-table-row` is 0,2,0, matching CM6's own rule; * appearing later in the cascade, our rule wins. * * This was caught only by live-browser inspection — jsdom doesn't * apply the themed default, so the original spike R4 / R5 tests pass * regardless of the bug. The DOM-level integration tests in * table-grid.test.ts also pass because they only count * `.cm-table-cell` query matches, not their layout. The fix is * idiomatic CSS specificity, not a structural change. */.cm-line.cm-table-row { display: flex; /* Explicit horizontal layout, no wrapping. Forces cells onto a * single horizontal axis regardless of viewport width or content. */ flex-direction: row; flex-wrap: nowrap; /* `nowrap` here applies to text directly in the row container (e.g., * stray text nodes between spans). Cells override back to `normal` * so long content inside a cell wraps within its cell, not pushing * the cell wider than its flex basis. */ white-space: nowrap; /* Span full content width regardless of cell sizes. Without explicit * `width: 100%` a flex container shrinks to its content. */ width: 100%; box-sizing: border-box; border-left: 1px solid var(--text-tertiary); border-right: 1px solid var(--text-tertiary); /* Inter-row horizontal divider. Without this, adjacent body rows * render as a single continuous block (no visible boundary between * them), making a 4-row table look like 1 row with multi-line cells. * Every row gets `border-bottom`, including the last row — which is * what visually closes the table on the bottom edge. */ border-bottom: 1px solid var(--text-tertiary); padding: 0;}
/* The separator row's `|---|---|` content is masked by the * cm-table-separator-anchor mark below (#269). Its `border-bottom` * would render as a thin extra line above the body row's own * `border-top` — remove it so the visible separator is just the * meeting of header `border-bottom` + body `border-top`. */.cm-line.cm-table-row.cm-table-separator-row { border-bottom: 0;}
/* The first row needs an explicit `border-top` since the row container's * `border-bottom` only handles the bottom edge; the header would * otherwise be unbordered on top. The last row's bottom edge already * comes from the shared `.cm-line.cm-table-row` rule above (no separate * rule needed). */.cm-table-row-first { border-top: 1px solid var(--text-tertiary); }
.cm-table-header-row { font-weight: 600; background-color: var(--code-bg-sunken);}
.cm-table-separator-row { /* WHOLE-LINE HIDE — see hidden-text-classes.ts idiom 2 (#269). * * VISUAL: the separator row collapses to zero height so the * header's `border-bottom` and body's `border-top` meet directly * with no visible gap (matches the pre-#269 visual layout). The * row's `.cm-line` and its child anchor span still exist in the * DOM but render with zero geometry. * * CARET-TARGETABILITY: caretRangeFromPoint cannot find a zero- * geometry line, but that is fine here because the cursor never * arrives at the separator via column-Y projection — the * table-aware ArrowUp/ArrowDown keymap (table-keybindings.ts) routes * directly to `state.doc.lineAt(struct.separator.from).from` via * `view.dispatch({selection})`, which bypasses geometry-based * placement entirely. So the line is reachable as a navigation stop * even though it is visually zero-height. * * The separator's `|---|---|` text is wrapped by a Decoration.mark * carrying `.cm-table-separator-anchor`. The mark keeps the source * text in the DOM (allowing the user to delete or edit it), and * the same zero-geometry treatment applies — `font-size: 0; * line-height: 0; overflow: hidden` on the anchor below. * * Side-effect: the marked text remains editable. Typing into the * separator demotes the table to a paragraph (consistent with how * HR `---` behaves in live-preview). * * Side pipes are dropped (`border-left: 0; border-right: 0`) so * the otherwise-blank separator row doesn't render boxed-in. */ min-height: 0; height: 0; line-height: 0; padding: 0; margin: 0; overflow: hidden; border-left: 0; border-right: 0; user-select: none;}
/* CURSOR-SKIP JUSTIFICATION (#269 idiom 2): the separator-row text * is wrapped by a Decoration.mark; CSS hides the `|---|---|` glyphs * visually. Geometry is zero-collapsed (`height: 0; line-height: 0; * font-size: 0; overflow: hidden`) so the row renders as a zero-px * stripe and the header `border-bottom` meets the body `border-top` * with no visible gap. The mark is caret-targetable as a navigation * stop because the table-aware ArrowUp/ArrowDown keymap (see * table-keybindings.ts) routes the cursor here via * `view.dispatch({selection})`, which does not require geometry — * not via column-Y projection through caretRangeFromPoint, which * would fail on a zero-geometry box. */.cm-table-separator-anchor { display: block; height: 0; line-height: 0; font-size: 0; overflow: hidden; color: transparent; user-select: none;}
.cm-table-pipe-hidden { /* WHOLE-LINE-PARTICIPANT HIDE — see hidden-text-classes.ts idiom 2 (#269). * * Pipes interleave with cell content on a row line. Pre-fix * `display: none` removed pipe characters from DOM layout entirely, * so coordsAtPos for offsets landing on pipes returned null and * arrow-key navigation could not find the row. * * Post-fix: keep pipes as zero-width flex items * (`flex: 0 0 0; min-width: 0; width: 0`) with `color: transparent` * and `user-select: none`. They remain in flex flow so CM6 resolves * coordinates, but consume no visual space. The cell-mark wrapper * around inter-pipe content still produces the single-flex-item-per-cell * invariant table-grid.ts depends on. */ flex: 0 0 0; min-width: 0; width: 0; color: transparent; user-select: none; /* No `display: none` — see comment above. */}
/* Each cell wrapper takes 1/N of the row width. * * `display: block` is critical: `<span>` elements default to inline, * and although flex items are normally blockified by the flex layout * algorithm, some browsers (and various interactions with default * `<span>` line-height and CM6's measure pass) leave them at intrinsic * inline-content size. Explicitly making the cell a block element * avoids that footgun. * * `flex-basis: 0%` (NOT `0`) is the spec-compliant value for "ignore * content size when distributing space" — `flex: 1 1 0` is interpreted * as `0%` by the spec but historical browser quirks have parsed * unitless `0` as `0px`-with-content-fallback. The explicit `0%` is * more reliable. * * `min-width: 0` lets long content (wider than its share) shrink rather * than push the cell wider than the basis. */.cm-table-cell { display: block; flex: 1 1 0%; min-width: 0; padding: 0.25em 0.5em; border-right: 1px solid var(--text-tertiary); overflow: hidden; /* Override the row's `white-space: nowrap` so long cell content * wraps within the cell rather than overflowing horizontally. */ white-space: normal; word-break: break-word; box-sizing: border-box;}
.cm-table-cell-align-left { text-align: left; }.cm-table-cell-align-center { text-align: center; }.cm-table-cell-align-right { text-align: right; }
/* Empty cells: the wrapping cm-table-cell mark contributes the flex item; * a CSS ::before with non-breaking space provides line height since the * mark itself wraps no visible chars. */.cm-table-cell-empty::before { content: '\00a0'; }
/* Zero-width anchor span emitted inside empty cells. For empty cells with * a whitespace gap, the wrapping cm-table-cell-empty mark already * contributes the flex item — the anchor is just a DOM marker. * For empty cells with NO gap (e.g. ||), the anchor itself becomes the * flex item; flex: 1 1 0 makes it consume its column's width and the * nbsp ::before keeps line height. */.cm-table-cell-empty-anchor { display: block; flex: 1 1 0%; min-width: 0; box-sizing: border-box;}.cm-table-cell-empty-anchor::before { content: '\00a0'; }
/* ---- Footnote ref + def (live-preview + read mode) ---- * * Refs render as inline superscript chips via Decoration.replace * widgets emitted by footnote-decoration.ts. Defs are line-decorated * (`Decoration.line` adds .cm-md-footnote-def to the line) — raw * `[^id]: body` text stays visible; CSS provides the visual grouping * (smaller font, left rule, dimmed) without hiding source. * * Source mode: the extension is mounted only via livePreview(), so * source mode mounts a different compartment without it and shows * raw `[^id]` / `[^id]: body` text uncolored by chrome. * * Read mode: same DOM/decorations as live-preview. The chip is a * `<sup>` element with cursor: pointer; the read-mode rule overrides * the global `cursor: default !important` on .cm-line for resolved * refs only (unresolved refs are not clickable so they keep default * cursor). * -------------------------------------------------------------------------- */
/* CURSOR-SKIP JUSTIFICATION (#269): Footnote refs sit in inline flow * with visible siblings (the surrounding paragraph text). The * `line-height: 0` here is for visual baseline trim, not whole-line * collapse. */.cm-md-footnote-ref { display: inline; /* The widget element is a <sup>. The UA stylesheet renders <sup> with * `font-size: smaller` (about 0.83x) and `vertical-align: super`. We * pin explicit values here for theme-stability — guarantees the chip * is the same size across themes that override `sup`, NOT to match * the UA default. 0.75em is intentionally smaller than UA-smaller so * the chip reads as a discrete badge. */ font-size: 0.75em; vertical-align: super; /* line-height: 0 prevents the superscript from forcing the surrounding * line box taller — same reasoning as .cm-inline-formatting-hidden * above. */ line-height: 0; color: var(--accent); cursor: pointer; text-decoration: none; user-select: none;}
/* Hover only on resolved chips; unresolved chips are non-clickable so * we exclude them from underline-on-hover. One rule with a :not() * selector replaces two cascading rules. */.cm-md-footnote-ref:hover:not(.cm-md-footnote-unresolved) { text-decoration: underline;}
/* Unresolved variant: muted + italic. Cursor is default (not a pointer) * because clicking does nothing — the ref has no def to scroll to. */.cm-md-footnote-ref.cm-md-footnote-unresolved { color: var(--text-tertiary); font-style: italic; cursor: default;}
/* Footnote def prefix marker (`[^id]:`). Discoverable click affordance * for def → ref navigation. The whole def line is dimmed via * .cm-md-footnote-def; the prefix is colored with the accent + dotted * underline on hover so users see "this is clickable, the body isn't". */.cm-md-footnote-def-marker { color: var(--accent); cursor: pointer;}
.cm-md-footnote-def-marker:hover { text-decoration: underline dotted;}
/* Definition lines: line decoration. Smaller font, left rule, dimmed. * The leading `[^id]:` prefix gets cursor:pointer in the read-mode and * live-preview overrides below — but only on the prefix, not the body. * That prefix-only click target is enforced in the JS click handler * (footnote-decoration.ts) which checks `posAtDOM` against the def's * recorded prefixEndOffset. * * No `display: inline-block` or other layout changes — the line stays * a normal block flow element. CM6's .cm-line is `display: block` by * default, which we keep. */.cm-md-footnote-def { font-size: 0.85em; color: var(--text-secondary); border-left: 2px solid var(--text-tertiary); padding-left: 0.5em;}
/* Read-mode override: resolved ref chips remain clickable (pointer * cursor) despite the global .cm-line { cursor: default !important } * applied in the read-mode block above. Unresolved refs stay default. */.cm6-editor-container[data-mode="read"] .cm-md-footnote-ref:not(.cm-md-footnote-unresolved) { cursor: pointer !important;}
/* ---- Image gallery widget (2+ embeds on same line) ---- * * Layout is adaptive — a ResizeObserver in the widget JS toggles between * cm-image-gallery-grid (side-by-side CSS grid) and cm-image-gallery-carousel * (horizontal-scroll flex) based on whether images can fit at their minimum * width (200px each). No hard-coded breakpoints or image-count thresholds. * * Near/far mode: * - Block mode (cursor on line): <div> wrapper, raw text visible above * - Inline mode (cursor elsewhere): <span> wrapper replacing embed text * -------------------------------------------------------------------------- */.cm-image-gallery { display: block; width: 100%; box-sizing: border-box; position: relative;}
.cm-image-gallery[data-mode="inline"] { display: inline-block; vertical-align: top;}
/* Items container — shared by both layouts */.cm-image-gallery-items { width: 100%;}
/* Grid layout (images fit side-by-side) */.cm-image-gallery-grid .cm-image-gallery-items { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0.5rem;}
/* Carousel layout (images too wide to fit; horizontal scroll) */.cm-image-gallery-carousel .cm-image-gallery-items { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; gap: 0.5rem; padding-bottom: 0.5rem; scrollbar-width: thin;}
.cm-image-gallery-carousel .cm-image-gallery-item { flex: 0 0 200px; scroll-snap-align: start;}
/* Scrollbar styling for carousel */.cm-image-gallery-carousel .cm-image-gallery-items::-webkit-scrollbar { height: 4px;}
.cm-image-gallery-carousel .cm-image-gallery-items::-webkit-scrollbar-track { background: var(--bg-secondary, #f0f0f0);}
.cm-image-gallery-carousel .cm-image-gallery-items::-webkit-scrollbar-thumb { background: var(--text-tertiary, #999); border-radius: 2px;}
/* Navigation buttons — always in the DOM, hidden for grid, shown for carousel. * The ResizeObserver swaps the class, so buttons appear/disappear automatically. * * CURSOR-SKIP JUSTIFICATION (#269): widget chrome — carousel nav button. * Hidden by default; shown via a sibling-state class. The line containing * the gallery widget has other visible content, so cursor-skip cannot * manifest. */.cm-image-gallery-nav { position: absolute; top: 50%; transform: translateY(-50%); z-index: 1; width: 2rem; height: 2rem; border: none; border-radius: 50%; background: var(--bg-primary); color: var(--text-primary); font-size: 1.25rem; cursor: pointer; opacity: 0; transition: opacity 0.2s ease; display: none; /* Hidden for grid; shown for carousel */ align-items: center; justify-content: center; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);}
/* Show nav buttons only in carousel mode */.cm-image-gallery-carousel .cm-image-gallery-nav { display: flex;}
/* Show nav on hover (desktop) and always on touch (no hover capability) */.cm-image-gallery-carousel:hover .cm-image-gallery-nav { opacity: 0.8;}
@media (hover: none) { .cm-image-gallery-carousel .cm-image-gallery-nav { opacity: 0.6; }}
.cm-image-gallery-nav:hover { opacity: 1 !important; background: var(--accent); color: #fff;}
.cm-image-gallery-nav-prev { left: 0.25rem;}
.cm-image-gallery-nav-next { right: 0.25rem;}
/* Gallery items */.cm-image-gallery-item { margin: 0; padding: 0; text-align: center;}
.cm-image-gallery-img { display: block; width: 100%; height: auto; object-fit: contain; border-radius: 0.25rem;}
.cm-image-gallery-caption { margin-top: 0.25rem; font-size: 0.85em; color: var(--text-secondary); font-style: italic;}
/* Loading state */.cm-image-gallery-item-loading-text { display: block; padding: 1rem; color: var(--text-tertiary); font-style: italic;}
/* Error state */.cm-image-gallery-item-error { padding: 0.5rem; border: 1px dashed var(--error); border-radius: 0.25rem; color: var(--error); font-size: 0.85em;}
/* ---- Unified frontmatter properties widget (live preview) ---- * * Rendered by root-frontmatter-widget.tsx via a StateField that block-replaces * the entire frontmatter range `[0, endOffset)` with a structured * properties table. CM6Editor excludes the extension in source mode. * * Mobile: layout is flex with `flex-wrap` on the value cell so chips * wrap naturally when narrow. No media queries needed for v1. * -------------------------------------------------------------------------- *//* No outer border / background / fixed corner radius. The widget sits * at the top of the doc; the visual context (under the title, before * the body) is enough boundary on its own. The earlier card-style * frame was visual clutter that didn't earn its weight. Margin only — * a small bottom gap separates the properties block from the body. */.cm-frontmatter-properties-table { display: block; width: 100%; margin: 0 0 1em; font-family: var(--font-text, inherit); /* #282: load-bearing rule is `overflow: hidden` — it clips child * overflow at the table boundary so wrapped rows stay inside the * editor instead of leaking past it and triggering whole-page * horizontal scroll on phones (<768px viewport). * * `max-width: 100%` is a defensive belt-and-suspenders cap. A * default block-level element with `width: auto` already fills * its parent's content box, so this is mostly redundant — but it * cheaply protects against a future change where the widget's * containing element gains an unexpected intrinsic-sizing path * (e.g. a CM6 contributor wraps the widget in `display: inline-block` * or `width: max-content`), in which case `max-width: 100%` would * still cap it at the parent width. */ overflow: hidden; max-width: 100%; box-sizing: border-box;}
/* Per-row layout. No bottom-border separator: rows read clearly * because each row's controls are aligned and the value column * provides its own visual rhythm. * * `align-items: flex-start` (#278, D7 round-3): when a value cell * wraps to multiple lines (e.g. a tags row with many chips on a narrow * viewport), `align-items: center` would center the icon and key input * against the wrapped cell's full height — they'd visibly drift down. * Anchoring everything to the row's top edge keeps the icon, key, and * row-× lined up with the value cell's first line. * * Per-child alignment refines this further: the icon uses * `align-self: baseline` so its glyph baselines against the key * input's text baseline (the icon is intentionally smaller, 0.9em, * which under raw flex-start would sit visibly above the key text). * Inputs and the checkbox sit at the row's top edge naturally. */.cm-frontmatter-prop-row { display: flex; align-items: flex-start; gap: 0.2em; padding: 0; /* #282: clip flex children so wrapped value content stays bounded by * the row instead of stretching the editor wider than the viewport. */ overflow: hidden;}
/* Type icon — chromeless. A small muted glyph that signals the * property type at a glance. Fixed-width column so keys stay * vertically aligned across rows regardless of which glyph each * row uses. No border, background, or hover effect; the icon is * purely informational, not interactive. * * `align-self: baseline` (D7 round-3): pairs with the row's * `align-items: flex-start` to put the icon's text baseline on the * key input's text baseline — without this the smaller-font icon * would appear to sit higher than the key when both are anchored to * the row top. */.cm-frontmatter-prop-icon { display: inline-flex; align-items: center; justify-content: center; align-self: baseline; width: 1.25em; flex: 0 0 auto; color: var(--text-tertiary); font-size: 0.9em; user-select: none;}
.cm-frontmatter-prop-icon svg { width: 0.78em; height: 0.78em;}
.cm-frontmatter-prop-icon-svg { align-self: center; height: 1.4em; line-height: 1;}
.cm-frontmatter-prop-row[data-type="date"]:has(.reams-frontmatter-date-error) > .cm-frontmatter-prop-icon-svg,.cm-frontmatter-prop-row[data-type="datetime"]:has(.reams-frontmatter-date-error) > .cm-frontmatter-prop-icon-svg { align-self: start; margin-block-start: 0.26em;}
/* Inline-editable key. Renders like a static label by default — * borderless, transparent background — and gains a focus ring on * focus to signal editability. * * Mobile-first sizing: the key column is narrow by default (4em ≈ * ~64px) so the value cell gets the lion's share of the row on a * ~360px-wide phone. Desktop widens it to 8em via the min-width * media query at the bottom of this section, restoring the wider * column for at-a-glance scanning. The 4em floor still fits typical * short Obsidian/Reams keys (`tags`, `date`) without truncation * — anything longer ellipsizes via the overflow rules below. */.cm-frontmatter-prop-key { flex: 0 0 4em; width: 4em; padding: 0 0.25em; background-color: transparent; color: var(--text-secondary); border: 1px solid transparent; border-radius: 2px; font: inherit; /* Long keys: truncate rather than wrap — keeps row height predictable. */ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}
.cm-frontmatter-prop-key:hover:not(:disabled) { border-color: var(--text-tertiary);}
.cm-frontmatter-prop-key:focus { outline: none; border-color: var(--accent); background-color: var(--bg-primary, transparent); color: var(--text-primary);}
.cm-frontmatter-prop-key-readonly { flex: 0 0 4em; width: 4em; min-width: 0; padding: 0 0.25em; color: var(--text-secondary); overflow-wrap: anywhere;}
.cm-frontmatter-prop-value { flex: 1 1 auto; display: flex; /* Full reflow: values wrap inside the row instead of creating a nested * horizontal-scroll lane. This makes tags and list items all visible at * once and lets long controls consume additional vertical space rather * than stealing horizontal swipe gestures. */ flex-wrap: wrap; align-items: flex-start; gap: 0.25em; row-gap: 0.25em; min-width: 0;}
/* Generic input (text, number, date, datetime). Borderless by default * so the row reads as a continuous strip; focus ring on the input * itself signals editability without per-cell chrome. * * Reflow mode uses full-width controls, so long values take vertical * space instead of creating a horizontal scroll surface. */.cm-frontmatter-prop-input { flex: 1 1 12em; width: 100%; min-width: 0; max-width: 100%; padding: 0 0.35em; background-color: transparent; color: var(--text-primary); border: 1px solid transparent; border-radius: 2px; font: inherit;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-input:not(textarea),.cm-root-frontmatter-surface .cm-frontmatter-prop-value > select,.cm-root-frontmatter-surface .reams-input-suggest > input,.cm-root-frontmatter-surface .reams-root-legacy-add-form > input,.cm-root-frontmatter-surface .reams-root-legacy-add-form > select { height: var(--reams-frontmatter-control-height); min-height: var(--reams-frontmatter-control-height); box-sizing: border-box;}
.cm-frontmatter-prop-input:hover:not(:disabled) { border-color: var(--text-tertiary);}
.cm-frontmatter-prop-input:focus { outline: none; border-color: var(--accent); background-color: var(--bg-primary, transparent);}
.cm-frontmatter-prop-readonly-value { min-width: 0; overflow-wrap: anywhere; white-space: pre-wrap;}
.cm-frontmatter-prop-checkbox { /* Match the in-editor task checkbox sizing; the accent-color is the * same so toggle visuals are consistent across the editor. */ width: 1em; height: 1em; margin: 0; accent-color: var(--accent); cursor: pointer; align-self: center;}
.cm-frontmatter-prop-row[data-type="checkbox"] .cm-frontmatter-prop-value { align-items: center; min-height: var(--reams-frontmatter-control-height, 1.4em);}
/* List/Tags chips reflow within the value lane so the full set remains * visible at once instead of requiring horizontal scrolling. */.cm-frontmatter-prop-list { flex-wrap: wrap;}
/* Chip — chromeless. The wrapper exists only to host the editable * input + the (×) hover button; visually each chip reads as a bare * value, separated from siblings by the value cell's flex `gap`. * Tag chips opt back into chrome via `.cm-frontmatter-tag-chip` * (color-mixed accent background + leading `#`). */.cm-frontmatter-prop-chip { display: inline-flex; align-items: center; gap: 0.15em; min-width: 0; max-width: 100%; padding: 0; color: var(--text-primary); user-select: none; /* Keep chips at their content width while allowing the parent value lane * to wrap them onto additional lines. */ flex: 0 0 auto;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-chip { height: var(--reams-frontmatter-control-height); min-height: var(--reams-frontmatter-control-height); box-sizing: border-box;}
/* Applied Types use the same colored chip as Records and the Types browser. */.cm-root-frontmatter-surface .cm-frontmatter-prop-chip.reams-type-chip { gap: 0.125rem; padding: 2px 2px 2px 6px; color: var(--reams-type-chip-text);}
.reams-root-type-chip > .cm-frontmatter-prop-chip-readonly-value { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis;}
.reams-root-type-chip > .cm-frontmatter-prop-chip-remove { flex: 0 0 auto;}
/* The chip's editable text input. Auto-sized by `field-sizing: content` * where supported; its HTML `size` attribute provides the fallback. * * No padding: the chip hugs its text exactly. The wrapper's flex * `gap` (0.15em) provides the only space between the input and the ×. * */.cm-frontmatter-prop-chip-input { /* No internal padding — the chip's content (input + ×) hugs the * input edges. Breathing room from the chip's left edge (when the * chip has a tinted background, e.g. tag chips) is provided by * `padding-left` on the WRAPPER (.cm-frontmatter-tag-chip), not * here, so that plain chrome-less list chips have no spurious * leading space. */ padding: 0; margin: 0; background-color: transparent; color: inherit; border: none; outline: none; font: inherit; /* Keep box-sizing explicit so a global reset can't surprise us. */ box-sizing: content-box; /* `field-sizing: content` makes the input grow/shrink to fit its * text content with no manual measurement (Chrome 123+, Safari * 17.4+, Firefox 123+). Older browsers use the input's `size`. */ field-sizing: content; /* Clamp each chip to the row width so a single unbreakable token does not * escape the wrapped layout and get hard-clipped by ancestor overflow. */ min-width: 0; min-inline-size: 1ch; max-width: 100%;}
.cm-frontmatter-prop-chip-input:focus { outline: none;}
/* Size editable chip values from their rendered text instead of the input's * character-count `size` heuristic, which leaves large gaps in proportional * fonts on browsers without `field-sizing: content`. */.cm-frontmatter-prop-chip-input-sizer { display: inline-grid; min-width: 1ch;}
.cm-frontmatter-prop-chip-input-sizer::after,.cm-frontmatter-prop-chip-input-sizer > .cm-frontmatter-prop-chip-input { grid-area: 1 / 1;}
.cm-frontmatter-prop-chip-input-sizer::after { content: attr(data-value); visibility: hidden; white-space: pre;}
.cm-frontmatter-prop-chip-input-sizer > .cm-frontmatter-prop-chip-input { width: 100%;}
.cm-frontmatter-prop-chip-trailing .cm-frontmatter-prop-chip-input { min-inline-size: 3ch;}
/* Suggestion inputs normally use the full row-control height. Inside the * padded add chip, match the intrinsic height of existing chip inputs. */.cm-root-frontmatter-surface .cm-frontmatter-prop-chip-trailing .reams-input-suggest { flex: 0 0 3.5ch; width: 3.5ch;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-chip-trailing .reams-input-suggest > .cm-frontmatter-prop-chip-input { width: 3.5ch; height: auto; min-height: 0; padding-inline: 0;}
.cm-root-frontmatter-surface .reams-record-readonly-value { padding-inline-start: var(--reams-frontmatter-value-inset); color: var(--text-tertiary);}
.cm-frontmatter-prop-chip-readonly-value { min-width: 0; overflow-wrap: anywhere;}
/* Trailing "+ add" empty chip discoverability (#280). The chrome-strip * pass left the trailing chip with no visual affordance beyond the * browser-default placeholder, which on most platforms renders very * faintly. On an empty list (`items: []`) the slot was essentially * invisible. * * Two reinforcements (squint test: should read as "add slot" without * focus): * 1. Explicit placeholder color — pin to text-tertiary + italic so * the `+ add` text is consistent across browsers and clearly * muted-but-visible. * 2. Leading `+` glyph rendered via ::before on the wrapper — same * mechanism the tag chip uses for its `#` glyph. Plain (non-tag) * trailing chips get an opacity-0.5 plus to indicate "add"; tag * trailing chips also get the plus (overriding the suppressed * `#` for the trailing-tag case below). * * Right-side breathing room: the trailing chip is the last element * before the row-× button. Without margin-right it crowds the remove * button on narrow viewports. 0.5em gives comfortable separation. * * Internal padding-right: the chip's input has `padding: 0` and uses * `field-sizing: content` so it hugs the placeholder text exactly. * Without internal padding-right the chip ends abruptly right after * the "add" text. 0.25em (4pt) adds a small interior trailing space * so the chip's bounds read as "+ add ⎵" rather than "+ add|". This * is internal breathing room; the margin-right above is external * separation from the row-× button. */.cm-frontmatter-prop-chip-trailing { margin-right: 0.5em; padding-right: 0.25em;}
.cm-frontmatter-prop-chip-trailing .cm-frontmatter-prop-chip-input::placeholder { color: var(--text-tertiary); font-style: italic; opacity: 1;}
.cm-frontmatter-prop-chip-trailing::before { content: '+'; display: inline-block; margin-right: 0.15em; color: var(--text-tertiary); opacity: 0.5; font-weight: 600; user-select: none;}
/* The × is always visible (no hover reveal). The earlier hover-only * affordance was confusing on touch devices and added visual jitter * when the cursor moved between chips. Steady-state visibility, with * an opacity bump on hover to signal interactivity. * * Default opacity 0.5 (#277): chip-× is dimmed by default to give the * row-× clear primacy. On a tags row with N chips the user sees N * dimmed chip-×s and ONE crisp row-× — the visual hierarchy reads * "remove a tag" (incidental) vs "remove the property" (deliberate) * even at a glance. Hover lifts the chip-× to full strength. */.cm-frontmatter-prop-chip-remove,.cm-frontmatter-prop-chip-remove-placeholder { display: inline-flex; align-items: center; justify-content: center; width: 1rem; height: 1rem; padding: 0; margin: 0; border: none; border-radius: 2px; background-color: transparent; color: var(--text-tertiary); opacity: 0.5; font: inherit; font-size: 1em; line-height: 1; cursor: pointer; /* Match the opacity-transition idiom used by sibling chips * (.cm-wikilink-chip et al.) — without this the dim→bright on hover * is abrupt (#D6 adversary fix). */ transition: opacity 0.1s ease;}
.cm-frontmatter-prop-chip-remove:hover,.cm-frontmatter-prop-chip-remove:focus-visible { color: var(--text-primary); opacity: 1;}
.cm-frontmatter-prop-chip > .cm-frontmatter-prop-chip-remove { margin-left: -0.05em;}
/* #282 round-3: chip-× lives inside .cm-frontmatter-prop-row which has * `overflow: hidden` (added in this fix). The default UA focus outline * would be clipped on top/bottom edges — same a11y bug round-2 fixed * for the sibling row-× and add-cancel buttons. Apply the same * inset-ring + tinted-background treatment here. The combined :hover, * :focus-visible rule above shares color+opacity changes between the * two states; this rule layers focus-only chrome on top so a keyboard * user can distinguish "focused" from "merely hovered." * * Source-order placement: this rule comes AFTER the combined * :hover, :focus-visible block so :focus-visible's box-shadow + * background paint over (not under) the shared color/opacity changes. * Specificity is identical (single pseudo-class), so order wins. */.cm-frontmatter-prop-chip-remove:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--accent); background-color: color-mix(in srgb, var(--accent) 12%, transparent);}
/* Frontmatter tags opt into the canonical `.reams-tag-chip` styling from * src/components/tag-chip.css. This local rule only preserves the widget's * editable flex layout. */.cm-frontmatter-tag-chip { display: inline-flex; gap: 0.125rem;}
/* Tag chips use a smaller font than the surrounding field controls. Keep * their control box on the shared height token so the tag and property remove * buttons have the same first-line center on every viewport. */.cm-root-frontmatter-surface .cm-frontmatter-tag-chip { height: var(--reams-frontmatter-control-height); min-height: var(--reams-frontmatter-control-height); box-sizing: border-box;}
/* Compatibility alias for tests and historical selectors. The canonical * prefix definition lives in `.reams-tag-chip-prefix::before`. */.cm-frontmatter-tag-chip::before { margin-right: 0.15em;}
/* When the tag chip is the trailing empty/placeholder slot, swap the * `#` prefix for a `+` glyph (#280): the slot is for ADDING a tag, * not yet a tag itself. Same color/opacity as the plain trailing * chip's `+` so the affordance reads identically across list and * tags variants. Specificity-wise this two-class selector overrides * the single-class `.cm-frontmatter-prop-chip-trailing::before` * defined below. */.cm-frontmatter-tag-chip.cm-frontmatter-prop-chip-trailing::before { content: '+'; color: var(--text-tertiary); opacity: 0.5; font-weight: 600;}
.cm-frontmatter-tag-chip.cm-frontmatter-prop-chip-readonly-spacer::before { content: '+'; color: var(--text-tertiary); opacity: 0.5; font-weight: 600;}
@media (max-width: 767px) { /* Mobile: tag chips can dominate the single-row scroll lane, so trim the * chrome slightly without changing the overall one-line interaction model. */ .cm-frontmatter-tag-chip { display: inline-flex; }}
.cm-frontmatter-tag-chip .cm-frontmatter-prop-chip-remove,.cm-frontmatter-tag-chip .cm-frontmatter-prop-chip-remove-placeholder,.reams-root-type-chip > .cm-frontmatter-prop-chip-remove { width: 0.75rem; height: 1rem; margin-left: 0; color: var(--reams-tag-chip-color, var(--accent));}
.reams-root-type-chip > .cm-frontmatter-prop-chip-remove { color: var(--reams-type-chip-text);}
.cm-frontmatter-tag-chip.reams-tag-chip { padding-left: var(--reams-tag-chip-padding-inline); padding-right: var(--reams-tag-chip-remove-padding-inline, 2px); color: var(--reams-tag-chip-color);}
@media (pointer: coarse) { .cm-frontmatter-tag-chip .cm-frontmatter-prop-chip-remove, .reams-root-type-chip > .cm-frontmatter-prop-chip-remove { position: relative; min-width: 0.75rem; min-height: 1em; }
.cm-frontmatter-tag-chip .cm-frontmatter-prop-chip-remove::after, .reams-root-type-chip > .cm-frontmatter-prop-chip-remove::after { content: ''; position: absolute; width: 24px; height: 24px; left: 50%; top: 50%; transform: translate(-50%, -50%); }}
.cm-frontmatter-tag-chip .cm-frontmatter-prop-chip-remove:hover { color: var(--text-primary);}
.reams-root-type-chip > .cm-frontmatter-prop-chip-remove:is(:hover, :focus-visible) { color: var(--reams-type-chip-text); opacity: 1;}
.reams-root-type-chip > .cm-frontmatter-prop-chip-remove:focus-visible { box-shadow: inset 0 0 0 2px color-mix(in oklch, var(--reams-type-color, var(--accent)) 60%, var(--text-primary)); background-color: color-mix(in oklch, var(--reams-type-color, var(--accent)) 18%, transparent);}
/* Per-row remove (× on the right edge of each property row). * Always visible — the earlier hover-reveal added jitter and didn't * work on touch devices. * * Visual hierarchy vs chip-× (#277): the row-× nukes the entire * property; the chip-× removes a single list item. Two dimensions of * differentiation: * 1. Position — `margin-left: auto` pushes the row-× to a fixed * right gutter so it never sits adjacent to chip-×s on the same * visual line. Even when a tags row's chips wrap, the row-× sits * in its own column at the row's right edge. * 2. Intensity — chip-×s are dimmed to opacity 0.5 (see below); the * row-× stays at full text-tertiary intensity. Combined with the * gutter this gives the row-× clear primacy even though the * chip-× shares the same color token. * * `align-self: flex-start` anchors the button to the row's first line * rather than centering against a wrapped value cell (#278). With the * row-level `align-items: flex-start` (D7 round-3) this is redundant * for the default case, but kept explicit so any future change to the * row's alignment can't silently re-introduce row-× drift on wrap. */.cm-frontmatter-prop-remove,.cm-frontmatter-prop-remove-placeholder { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; align-self: flex-start; margin-left: auto; width: 1.4em; height: var(--reams-frontmatter-control-height, 1.4em); padding: 0; border: none; border-radius: 2px; background-color: transparent; color: var(--text-tertiary); font: inherit; font-size: 1em; line-height: 1; cursor: pointer;}
.cm-root-frontmatter-surface .cm-frontmatter-prop-row:has(> .cm-frontmatter-prop-value > .reams-yaml-object-summary) > .cm-frontmatter-prop-remove { height: 1.4em;}
.cm-frontmatter-prop-remove:hover { color: var(--text-primary);}
/* #282: explicit :focus-visible with an INSET focus indicator (box-shadow * + color, not outline). The row's `overflow: hidden` (added in the same * fix) would clip a default UA outline on top/bottom edges, breaking * keyboard a11y on the row-× button. Use box-shadow inset which paints * inside the element's own box and survives ancestor clipping. * * 2px ring (not 1px) because the button is small (1.4em ≈ 22px on * default 16px root font) and a single-pixel ring against the row's * background is marginal at WCAG 2.4.7 threshold. The translucent * accent-tinted background reinforces focus distinctly from :hover * (which is color-only). */.cm-frontmatter-prop-remove:focus-visible { outline: none; color: var(--text-primary); box-shadow: inset 0 0 0 2px var(--accent); background-color: color-mix(in srgb, var(--accent) 12%, transparent);}
@media (pointer: coarse) { .cm-frontmatter-prop-chip-remove { position: relative; min-width: 1rem; min-height: 1rem; }
.cm-frontmatter-prop-chip-remove::after { content: ''; position: absolute; width: 24px; height: 24px; left: 50%; top: 50%; transform: translate(-50%, -50%); }
.cm-frontmatter-prop-remove { min-width: 24px; min-height: 24px; }}
/* Add-property row at the bottom. Chromeless: no top border, no * dedicated background. Two states: * - Idle: `+ Add property` button only. * - Active: name input | value input | × cancel button. */.cm-frontmatter-prop-add { display: flex; align-items: center; gap: 0.5em; padding: 0.1em 0;}
.cm-frontmatter-prop-add-btn,.cm-frontmatter-prop-add-placeholder { background-color: transparent; color: var(--text-tertiary); border: none; font: inherit; cursor: pointer; padding: 0;}
.cm-frontmatter-prop-add-btn:hover,.cm-frontmatter-prop-add-btn:focus { color: var(--accent); outline: none;}
/* Active-mode name field — same width as a regular property key, * mirroring the mobile-first 4em / desktop 8em sizing pattern below. * Borderless by default like other key inputs; gains the focus ring * on focus. */.cm-frontmatter-prop-add-name { flex: 0 0 4em; width: 4em; padding: 0 0.25em; background-color: transparent; color: var(--text-primary); border: 1px solid transparent; border-radius: 2px; font: inherit;}
.cm-frontmatter-prop-add-name:focus { outline: none; border-color: var(--accent);}
/* Active-mode value field — fills remaining row width. */.cm-frontmatter-prop-add-value { flex: 1 1 auto; min-width: 4em; padding: 0 0.35em; background-color: transparent; color: var(--text-primary); border: 1px solid transparent; border-radius: 2px; font: inherit;}
.cm-frontmatter-prop-add-value:hover { border-color: var(--text-tertiary);}
.cm-frontmatter-prop-add-value:focus { outline: none; border-color: var(--accent);}
.cm-frontmatter-prop-add-cancel { flex: 0 0 auto; width: 1.4em; height: 1.4em; padding: 0; border: none; border-radius: 2px; background-color: transparent; color: var(--text-tertiary); font: inherit; font-size: 1em; line-height: 1; cursor: pointer;}
.cm-frontmatter-prop-add-cancel:hover { color: var(--text-primary);}
/* #282: inset focus indicator for the same reason as .cm-frontmatter-prop-remove. * The add-property row sits inside `.cm-frontmatter-properties-table` which * has `overflow: hidden`; an outline-style focus ring would clip. Same * 2px-ring + tinted-background treatment for a11y parity. */.cm-frontmatter-prop-add-cancel:focus-visible { outline: none; color: var(--text-primary); box-shadow: inset 0 0 0 2px var(--accent); background-color: color-mix(in srgb, var(--accent) 12%, transparent);}
/* Read-mode treatment. The widget JS sets `disabled` on every input; * the additional class lets us tone down the entire wrapper. */.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-input:disabled,.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-key:disabled,.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-chip-input:disabled,.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-checkbox:disabled { cursor: default; opacity: 0.85;}
/* Read mode drops the interactive affordance paint, so reserve the same right * gutter the edit-mode controls occupy to keep table bounds stable. */.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-value { padding-right: 8px; box-sizing: border-box;}
.cm-frontmatter-prop-remove-placeholder,.cm-frontmatter-prop-chip-remove-placeholder,.cm-frontmatter-prop-chip-readonly-spacer,.cm-frontmatter-prop-add-placeholder { visibility: hidden; pointer-events: none;}
.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-input:hover:disabled,.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-key:hover:disabled { border-color: transparent;}
/* CURSOR-SKIP JUSTIFICATION (#269): widget chrome inside the * frontmatter properties widget — these are the per-row "remove" and * "add" buttons that allow editing in live-preview mode. The * `cm-frontmatter-properties-table-readonly` wrapper is set when the * editor is in read mode, where the widget itself is rendered * read-only and the editing affordances are hidden via `display: none`. * None of these elements are caret-targetable text in any mode (they * are buttons inside a CM6 widget that's rendered with * contenteditable=false), so `display: none` cannot cause cursor- * skip — the widget chrome is outside the editor's caret flow. */.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-remove,.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-chip-remove,.cm-frontmatter-properties-table-readonly .cm-frontmatter-prop-chip-trailing { display: none;}
/* Frontmatter widget: desktop column widening. Mobile-first defaults * give the key column a tight 4em (~64px on default font size) so * the value cell isn't crowded on a ~360px phone viewport. At * tablet/desktop widths there's plenty of room for a wider key * column that improves at-a-glance scannability across rows. * * Breakpoint: 768px matches the existing mobile/desktop split used * by the rest of cm6-editor.css (see image-viewer + toolbar * clearance rules below). */@media (min-width: 768px) { .cm-frontmatter-prop-key, .cm-frontmatter-prop-key-readonly, .cm-frontmatter-prop-add-name { flex-basis: 8em; width: 8em; }}
/* ---- Image viewer lightbox overlay (#231) ---- * * Fullscreen overlay for viewing image embeds at natural resolution. * Triggered by clicking any rendered image embed or gallery image. * Vanilla-DOM component (ImageViewerOverlay class), appended to * document.body — NOT inside the CM6 editor container. * * z-index: 2100 — above GestureIndicator (2000), settings modal (1000), * and fuzzy search overlay (1000). The lightbox should always be the * topmost layer since it's a transient fullscreen view. * -------------------------------------------------------------------------- *//* The overlay root doubles as the backdrop — its dark background is the * click-to-dismiss target. No separate backdrop div is needed; a sibling * backdrop would be unreachable behind the full-viewport content wrapper * in the stacking context. */.image-viewer-overlay { position: fixed; inset: 0; z-index: 2100; display: grid; grid-template-rows: 1fr auto; grid-template-columns: 1fr; background: rgba(0, 0, 0, 0.9);}
/* Content wrapper is pointer-transparent so clicks on the backdrop area * (the overlay root behind the content) reach the overlay's click-to-dismiss * handler. Interactive children restore pointer-events: auto individually. */.image-viewer-content { /* NOTE: position: relative removed intentionally. The close button uses * absolute positioning, but it now falls back to the fixed-positioned * .image-viewer-overlay ancestor as its containing block, which works * correctly since the overlay covers the viewport. */ grid-row: 1; display: flex; align-items: center; justify-content: center; min-height: 60vh; /* Spec: image must retain at least 60% of viewport */ padding: 2rem; box-sizing: border-box; pointer-events: none;}
.image-viewer-img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 4px; user-select: none; -webkit-user-select: none; pointer-events: auto;}
/* Close button — top-right corner. * * CURSOR-SKIP JUSTIFICATION (#269): overlay chrome — this button is a * `<button>` inside the fullscreen image viewer overlay, NOT inside * the editor's `.cm-line` flow. The `font-size: 0; line-height: 0` * declarations are flex-centering hygiene (the button has no text * content, only a child SVG icon), not a content-collapse. Cursor- * skip is impossible here because the overlay is rendered outside * the editor surface entirely — no `.cm-line` parent, no caret * navigation through this element. */.image-viewer-close { position: absolute; top: 1rem; right: 1rem; z-index: 1; width: 2.5rem; height: 2.5rem; border: none; /* Squarish to match the rest of the app's button design language * (Header, Ribbon, Settings, Modal — all 4px radius). */ border-radius: 4px; background: rgba(255, 255, 255, 0.15); /* Icon stroke uses currentColor (set by setIcon defaults), so this * controls the SVG glyph color. */ color: #fff; /* Reset font properties so any flex-centering math isn't thrown off * by inherited line-height (the button has no text content; the SVG * icon child sits in the flex centering context). */ font-size: 0; line-height: 0; padding: 0; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.15s ease; pointer-events: auto;}
.image-viewer-close:hover { background: rgba(255, 255, 255, 0.3);}
.image-viewer-close:focus-visible { outline: 2px solid var(--accent, #4a9eff); outline-offset: 2px;}
/* Nav buttons — left/right edges. * * CURSOR-SKIP JUSTIFICATION (#269): overlay chrome (same rationale as * `.image-viewer-close`). The `font-size: 0; line-height: 0` is * flex-centering hygiene for the SVG-icon-only button; the element * is outside the editor's `.cm-line` flow and not caret-navigable. */.image-viewer-nav { position: fixed; top: 50%; transform: translateY(-50%); z-index: 1; width: 3rem; height: 3rem; border: none; /* Squarish to match the rest of the app (4px radius idiom) */ border-radius: 4px; background: rgba(255, 255, 255, 0.15); color: #fff; /* Reset font props (button has no text, only an SVG icon child) */ font-size: 0; line-height: 0; padding: 0; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.15s ease; pointer-events: auto;}
.image-viewer-nav:hover { background: rgba(255, 255, 255, 0.3);}
.image-viewer-nav:focus-visible { outline: 2px solid var(--accent, #4a9eff); outline-offset: 2px;}
.image-viewer-nav-prev { left: 1rem;}
.image-viewer-nav-next { right: 1rem;}
/* Counter — bottom center. No pointer-events needed as it's not interactive. */.image-viewer-counter { position: absolute; bottom: 1rem; left: 50%; transform: translateX(-50%); color: rgba(255, 255, 255, 0.7); font-size: 0.875rem; font-variant-numeric: tabular-nums; user-select: none; -webkit-user-select: none;}
/* Mobile: larger touch targets. Width/height bumped above the 44px iOS HIG * threshold; SVG icon size is set in JS via setIcon(..., 24/28) and stays * the same across breakpoints — only the button hit area grows on mobile. */@media (max-width: 767px) { .image-viewer-content { padding: 1rem; }
.image-viewer-close { width: 3rem; height: 3rem; }
.image-viewer-nav { width: 3rem; height: 3rem; }
.image-viewer-nav-prev { left: 0.5rem; }
.image-viewer-nav-next { right: 0.5rem; }}
/* Touch devices: always show nav (no hover) */@media (hover: none) { .image-viewer-nav { background: rgba(255, 255, 255, 0.2); }}
/* Respect reduced motion preference */@media (prefers-reduced-motion: reduce) { .image-viewer-overlay, .image-viewer-close, .image-viewer-nav { transition: none; }}
/* Cursor affordance: images in the editor become clickable in live-preview * and read modes. The pointer cursor signals that clicking opens the lightbox. */.cm-image-embed-img,.cm-image-gallery-img { cursor: pointer;}
/* ---- Image viewer caption (#281) ---- * * Displays the current image's alt text at the viewport bottom. * Click to expand for full text; click again to collapse. * Hidden entirely when alt is empty or 'Image' (the fallback). * -------------------------------------------------------------------------- */.image-viewer-caption { grid-row: 2; position: relative; background: rgba(0, 0, 0, 0.8); color: rgba(255, 255, 255, 0.9); padding: 0.75rem 1rem; font-size: 0.9375rem; line-height: 1.4; text-align: center; cursor: pointer; user-select: none; -webkit-user-select: none; pointer-events: auto;
/* Collapsed state: single line, ellipsis truncation */ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
/* Smooth expand/collapse transition */ transition: max-height 0.2s ease, white-space 0s ease 0.2s; max-height: 3rem; /* ~1 line + padding */}
.image-viewer-caption.expanded { white-space: normal; overflow-y: auto; max-height: 40vh; text-align: left; padding: 1rem; transition: max-height 0.2s ease, white-space 0s ease;}
/* Scroll indicator: fade gradient overlay at bottom when content overflows. * Uses ::after pseudo-element to avoid obscuring the actual text content. */.image-viewer-caption.expanded.scrollable { padding-bottom: 2.5rem; /* Make room for the gradient overlay */}
.image-viewer-caption.expanded.scrollable::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 2rem; background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent); pointer-events: none;}
/* Hover state for collapsed caption — visual affordance for click */.image-viewer-caption:not(.expanded):hover { background: rgba(0, 0, 0, 0.9);}
/* Focus-visible for keyboard navigation */.image-viewer-caption:focus-visible { outline: 2px solid var(--accent, #4a9eff); outline-offset: -2px;}
/* Mobile: slightly larger touch target */@media (max-width: 767px) { .image-viewer-caption { padding: 1rem; font-size: 1rem; }}
/* Counter positioning: move above caption when caption is visible. * The counter (.image-viewer-counter) is positioned at bottom: 1rem by default. * When caption occupies grid row 2, we reposition the counter above it. */.image-viewer-overlay:has(.image-viewer-caption:not([hidden])) .image-viewer-counter { bottom: auto; /* Position counter just above the caption. Since caption is in grid row 2 * and can expand up to 40vh, we use a safe offset from the caption top. */ top: calc(100% - 60vh - 2rem); /* Above the min-height content area */}
/* Fallback for browsers without :has() support: hide counter when caption visible. * This is a graceful degradation — counter info is duplicated in caption context. */@supports not selector(:has(*)) { .image-viewer-caption ~ .image-viewer-counter { /* CURSOR-SKIP JUSTIFICATION (#269): overlay chrome — * `.image-viewer-counter` is a sibling element of * `.image-viewer-caption` inside the fullscreen image-viewer * overlay, NOT inside the editor's `.cm-line` flow. The counter * is read-only metadata text and the editor caret never traverses * the overlay surface. */ display: none; }}
/* Respect reduced motion preference */@media (prefers-reduced-motion: reduce) { .image-viewer-caption { transition: none; }}
/* --------------------------------------------------------------------------- * Tag decoration (#261) * * Inline #tag decorations in live-preview mode. Uses Decoration.mark with * class .cm-md-tag. Source mode shows raw text (extension not mounted). * * Styling matches frontmatter tag chips (.cm-frontmatter-tag-chip): * - Tinted accent background (color-mix 12%) * - Accent border (color-mix 40%) * - Accent text color * * Uses color-mix() which is widely supported (Chrome 111+, Safari 16.2+, * Firefox 113+). Falls back gracefully in older browsers. * --------------------------------------------------------------------------- *//* --------------------------------------------------------------------------- * Tag suggester dropdown * * Shares VISUAL styling with .cm-md-tag at rest, but uses a DEDICATED class * (.cm-tag-suggest-chip) so the dropdown's selected-row accent background * can override the chip's accent text/background WITHOUT affecting inline * .cm-md-tag (adversary fix C1 + M5). * * The dropdown row has THREE states from the dispatcher: * - rest: transparent background — chip uses accent tint as designed * - hover: `var(--bg-tertiary)` — neutral grey, chip still readable * - selected: `var(--accent)` — accent on accent → chip needs override * * The selected-state override uses `var(--accent-foreground)` so a theme * with a different accent foreground (e.g. dark text on a light accent) * gets the right contrast — NOT a literal white (which only works for * dark themes with light accent foregrounds). (adversary C1-residual) * --------------------------------------------------------------------------- */.cm-tag-suggest-chip { display: inline-block; padding: 2px 6px; border-radius: 4px; background: color-mix(in srgb, var(--accent) 12%, transparent); border: 1px solid color-mix(in srgb, var(--accent) 40%, transparent); color: var(--accent); font-size: 0.85em; white-space: nowrap;}
/* Selected-row override. * * The dispatcher sets the row to `background: var(--accent)`, * `color: var(--accent-foreground)`. The at-rest chip (accent text on * 12%-accent background) becomes invisible under that — accent on accent. * * Override the chip to use the accent-foreground color for both text and * its translucent background. With `color: inherit`, the chip's text * picks up the row's `var(--accent-foreground)` value automatically — so * a theme that uses dark accent-foreground on a light accent gets correct * contrast. The 22%-foreground translucent fill provides a subtle chip * outline against the solid accent row, and `border-color: transparent` * keeps the chip from competing with the row's edge. */.cm-editor-suggest-item.is-selected .cm-tag-suggest-chip { /* Browsers without color-mix() support (Safari < 16.2, Firefox < 113) * fall back to the rgba() declaration. Modern browsers use the * theme-aware color-mix() override. The fallback assumes a light * accent-foreground; on themes with dark accent-foreground the * fallback chip will be slightly less readable but still distinct * from the row background. (Adversary Mi2: pattern matches the * callout-bg fallbacks elsewhere in this file.) */ background: rgba(255, 255, 255, 0.22); background: color-mix(in srgb, var(--accent-foreground, #ffffff) 22%, transparent); border-color: transparent; color: inherit;}
/* Touch / coarse-pointer enforcement of WCAG 2.5.5 enhanced tap target. * Without this the dispatcher row is ~34px tall (padding 8px + chip 18px), * below the 44px guidance. Other components in the project enforce the * same minimum on coarse-pointer devices. */@media (pointer: coarse) { .cm-editor-suggest-item.cm-tag-suggest-item { min-height: 44px; }}